[英]What is the difference between canonical name, simple name and class name in Java Class?
In Java, what is the difference between these:在Java中,这些有什么区别:
Object o1 = ....
o1.getClass().getSimpleName();
o1.getClass().getName();
o1.getClass().getCanonicalName();
I have checked the Javadoc multiple times and yet this never explains it well.我已经多次检查了 Javadoc,但这从来没有很好地解释它。 I also ran a test and that didn't reflect any real meaning behind the way these methods are called.
我还进行了一个测试,这并没有反映出调用这些方法的方式背后的任何真正含义。
If you're unsure about something, try writing a test first.如果您不确定某事,请先尝试编写测试。
I did this:我这样做了:
class ClassNameTest {
public static void main(final String... arguments) {
printNamesForClass(
int.class,
"int.class (primitive)");
printNamesForClass(
String.class,
"String.class (ordinary class)");
printNamesForClass(
java.util.HashMap.SimpleEntry.class,
"java.util.HashMap.SimpleEntry.class (nested class)");
printNamesForClass(
new java.io.Serializable(){}.getClass(),
"new java.io.Serializable(){}.getClass() (anonymous inner class)");
}
private static void printNamesForClass(final Class<?> clazz, final String label) {
System.out.println(label + ":");
System.out.println(" getName(): " + clazz.getName());
System.out.println(" getCanonicalName(): " + clazz.getCanonicalName());
System.out.println(" getSimpleName(): " + clazz.getSimpleName());
System.out.println(" getTypeName(): " + clazz.getTypeName()); // added in Java 8
System.out.println();
}
}
Prints:印刷:
int.class (primitive):
getName(): int
getCanonicalName(): int
getSimpleName(): int
getTypeName(): int
String.class (ordinary class):
getName(): java.lang.String
getCanonicalName(): java.lang.String
getSimpleName(): String
getTypeName(): java.lang.String
java.util.HashMap.SimpleEntry.class (nested class):
getName(): java.util.AbstractMap$SimpleEntry
getCanonicalName(): java.util.AbstractMap.SimpleEntry
getSimpleName(): SimpleEntry
getTypeName(): java.util.AbstractMap$SimpleEntry
new java.io.Serializable(){}.getClass() (anonymous inner class):
getName(): ClassNameTest$1
getCanonicalName(): null
getSimpleName():
getTypeName(): ClassNameTest$1
There's an empty entry in the last block where getSimpleName
returns an empty string.最后一个块中有一个空条目,其中
getSimpleName
返回一个空字符串。
The upshot looking at this is:看这个的结果是:
- the name is the name that you'd use to dynamically load the class with, for example, a call to
Class.forName
with the defaultClassLoader
.name是您用于动态加载类的名称,例如,使用默认
ClassLoader
调用Class.forName
。 Within the scope of a certainClassLoader
, all classes have unique names.在某个
ClassLoader
的范围内,所有类都有唯一的名称。- the canonical name is the name that would be used in an import statement.
规范名称是在导入语句中使用的名称。 It might be useful during
toString
or logging operations.在
toString
或日志记录操作期间它可能很有用。 When thejavac
compiler has complete view of a classpath, it enforces uniqueness of canonical names within it by clashing fully qualified class and package names at compile time.当
javac
编译器拥有类路径的完整视图时,它通过在编译时冲突完全限定的类和包名称来强制其中规范名称的唯一性。 However JVMs must accept such name clashes, and thus canonical names do not uniquely identify classes within aClassLoader
.但是 JVM 必须接受这种名称冲突,因此规范名称不能唯一标识
ClassLoader
中的类。 (In hindsight, a better name for this getter would have beengetJavaName
; but this method dates from a time when the JVM was used solely to run Java programs.)(事后看来,这个 getter 的更好名称应该是
getJavaName
;但这个方法可以追溯到 JVM 仅用于运行 Java 程序的时候。)- the simple name loosely identifies the class, again might be useful during
toString
or logging operations but is not guaranteed to be unique.简单的名称松散地标识类,在
toString
或日志记录操作期间可能很有用,但不能保证是唯一的。- the type name returns "an informative string for the name of this type", "It's like
toString
: it's purely informative and has no contract value".类型名称返回“此类型名称的信息字符串”,“就像
toString
:它纯粹是信息性的,没有合同价值”。 (as written by sir4ur0n)(由 sir4ur0n 撰写)
Also you can commonly reference the Java Language Specification documentation for these types technical Java API details:您还可以通常参考 Java 语言规范文档以了解这些类型的技术 Java API 详细信息:
- Here's the Java 11 Specification on this subject matter: https://docs.oracle.com/javase/specs/jls/se11/html/jls-6.html#jls-6.7
这是关于此主题的 Java 11规范: https ://docs.oracle.com/javase/specs/jls/se11/html/jls-6.html#jls-6.7
Example 6.7-2.
andExample 6.7-2.
和
Example 6.7-2.
goes overFully Qualified Names
andFully Qualified Names v. Canonical Name
respectively分别遍历
Fully Qualified Names
和Fully Qualified Names v. Canonical Name
Adding local classes, lambdas and the toString()
method to complete the previous two answers.添加本地类、lambdas 和
toString()
方法来完成前两个答案。 Further, I add arrays of lambdas and arrays of anonymous classes (which do not make any sense in practice though):此外,我添加了 lambda 数组和匿名类数组(尽管在实践中没有任何意义):
package com.example;
public final class TestClassNames {
private static void showClass(Class<?> c) {
System.out.println("getName(): " + c.getName());
System.out.println("getCanonicalName(): " + c.getCanonicalName());
System.out.println("getSimpleName(): " + c.getSimpleName());
System.out.println("toString(): " + c.toString());
System.out.println();
}
private static void x(Runnable r) {
showClass(r.getClass());
showClass(java.lang.reflect.Array.newInstance(r.getClass(), 1).getClass()); // Obtains an array class of a lambda base type.
}
public static class NestedClass {}
public class InnerClass {}
public static void main(String[] args) {
class LocalClass {}
showClass(void.class);
showClass(int.class);
showClass(String.class);
showClass(Runnable.class);
showClass(SomeEnum.class);
showClass(SomeAnnotation.class);
showClass(int[].class);
showClass(String[].class);
showClass(NestedClass.class);
showClass(InnerClass.class);
showClass(LocalClass.class);
showClass(LocalClass[].class);
Object anonymous = new java.io.Serializable() {};
showClass(anonymous.getClass());
showClass(java.lang.reflect.Array.newInstance(anonymous.getClass(), 1).getClass()); // Obtains an array class of an anonymous base type.
x(() -> {});
}
}
enum SomeEnum {
BLUE, YELLOW, RED;
}
@interface SomeAnnotation {}
This is the full output:这是完整的输出:
getName(): void
getCanonicalName(): void
getSimpleName(): void
toString(): void
getName(): int
getCanonicalName(): int
getSimpleName(): int
toString(): int
getName(): java.lang.String
getCanonicalName(): java.lang.String
getSimpleName(): String
toString(): class java.lang.String
getName(): java.lang.Runnable
getCanonicalName(): java.lang.Runnable
getSimpleName(): Runnable
toString(): interface java.lang.Runnable
getName(): com.example.SomeEnum
getCanonicalName(): com.example.SomeEnum
getSimpleName(): SomeEnum
toString(): class com.example.SomeEnum
getName(): com.example.SomeAnnotation
getCanonicalName(): com.example.SomeAnnotation
getSimpleName(): SomeAnnotation
toString(): interface com.example.SomeAnnotation
getName(): [I
getCanonicalName(): int[]
getSimpleName(): int[]
toString(): class [I
getName(): [Ljava.lang.String;
getCanonicalName(): java.lang.String[]
getSimpleName(): String[]
toString(): class [Ljava.lang.String;
getName(): com.example.TestClassNames$NestedClass
getCanonicalName(): com.example.TestClassNames.NestedClass
getSimpleName(): NestedClass
toString(): class com.example.TestClassNames$NestedClass
getName(): com.example.TestClassNames$InnerClass
getCanonicalName(): com.example.TestClassNames.InnerClass
getSimpleName(): InnerClass
toString(): class com.example.TestClassNames$InnerClass
getName(): com.example.TestClassNames$1LocalClass
getCanonicalName(): null
getSimpleName(): LocalClass
toString(): class com.example.TestClassNames$1LocalClass
getName(): [Lcom.example.TestClassNames$1LocalClass;
getCanonicalName(): null
getSimpleName(): LocalClass[]
toString(): class [Lcom.example.TestClassNames$1LocalClass;
getName(): com.example.TestClassNames$1
getCanonicalName(): null
getSimpleName():
toString(): class com.example.TestClassNames$1
getName(): [Lcom.example.TestClassNames$1;
getCanonicalName(): null
getSimpleName(): []
toString(): class [Lcom.example.TestClassNames$1;
getName(): com.example.TestClassNames$$Lambda$1/1175962212
getCanonicalName(): com.example.TestClassNames$$Lambda$1/1175962212
getSimpleName(): TestClassNames$$Lambda$1/1175962212
toString(): class com.example.TestClassNames$$Lambda$1/1175962212
getName(): [Lcom.example.TestClassNames$$Lambda$1;
getCanonicalName(): com.example.TestClassNames$$Lambda$1/1175962212[]
getSimpleName(): TestClassNames$$Lambda$1/1175962212[]
toString(): class [Lcom.example.TestClassNames$$Lambda$1;
So, here are the rules.所以,这里是规则。 First, lets start with primitive types and
void
:首先,让我们从原始类型和
void
开始:
void
, all the four methods simply returns its name.void
,则所有四个方法都只返回其名称。 Now the rules for the getName()
method:现在
getName()
方法的规则:
getName()
) that is the package name followed by a dot (if there is a package), followed by the name of its class-file as generated by the compiler (whithout the suffix .class
).getName()
返回),它是包名称后跟一个点(如果有是一个包),后跟编译器生成的类文件的名称(没有后缀.class
)。 If there is no package, it is simply the name of the class-file.$
in its class-file name.$
。 Note that for anonymous classes, the class name would end with a dollar-sign followed by a number.$$Lambda$
, followed by a number, followed by a slash, followed by another number.$$Lambda$
,然后是一个数字,然后是一个斜线,然后是另一个数字。Z
for boolean
, B
for byte
, S
for short
, C
for char
, I
for int
, J
for long
, F
for float
and D
for double
.Z
代表boolean
, B
代表byte
, S
代表short
, C
代表char
, I
代表int
, J
代表long
, F
代表float
, D
代表double
。 For non-array classes and interfaces the class descriptor is L
followed by what is given by getName()
followed by ;
L
后跟getName()
给出的内容,后跟;
. [
followed by the class descriptor of the component type (which may be itself another array class).[
后跟组件类型的类描述符(它本身可能是另一个数组类)。getName()
method returns its class descriptor.getName()
方法返回其类描述符。 This rule seems to fail only for array classes whose the component type is a lambda (which possibly is a bug), but hopefully this should not matter anyway because there is no point even on the existence of array classes whose component type is a lambda. Now, the toString()
method:现在,
toString()
方法:
toString()
returns "interface " + getName()
.toString()
返回"interface " + getName()
。 If it is a primitive, it returns simply getName()
.getName()
。 If it is something else (a class type, even if it is a pretty weird one), it returns "class " + getName()
."class " + getName()
。 The getCanonicalName()
method: getCanonicalName()
方法:
getCanonicalName()
method returns just what the getName()
method returns.getCanonicalName()
方法只返回getName()
方法返回的内容。getCanonicalName()
method returns null
for anonymous or local classes and for array classes of those. getCanonicalName()
方法为匿名类或本地类以及它们的数组类返回null
。getCanonicalName()
method returns what the getName()
method would replacing the compiler-introduced dollar-signs by dots.getCanonicalName()
方法返回getName()
方法将用点替换编译器引入的美元符号的内容。getCanonicalName()
method returns null
if the canonical name of the component type is null
.null
,则getCanonicalName()
方法返回null
。 Otherwise, it returns the canonical name of the component type followed by []
.[]
。 The getSimpleName()
method: getSimpleName()
方法:
getSimpleName()
returns the name of the class as written in the source file.getSimpleName()
返回源文件中写入的类的名称。getSimpleName()
returns an empty String
.getSimpleName()
返回一个空的String
。getSimpleName()
just returns what the getName()
would return without the package name.getSimpleName()
只返回getName()
在没有包名的情况下返回的内容。 This do not makes much sense and looks like a bug for me, but there is no point in calling getSimpleName()
on a lambda class to start with.getSimpleName()
并没有任何意义。getSimpleName()
method returns the simple name of the component class followed by []
.getSimpleName()
方法返回组件类的简单名称,后跟[]
。 This have the funny/weird side-effect that array classes whose component type is an anonymous class have just []
as their simple names.[]
作为它们的简单名称。In addition to Nick Holt's observations, I ran a few cases for Array
data type:除了 Nick Holt 的观察之外,我还针对
Array
数据类型运行了一些案例:
//primitive Array
int demo[] = new int[5];
Class<? extends int[]> clzz = demo.getClass();
System.out.println(clzz.getName());
System.out.println(clzz.getCanonicalName());
System.out.println(clzz.getSimpleName());
System.out.println();
//Object Array
Integer demo[] = new Integer[5];
Class<? extends Integer[]> clzz = demo.getClass();
System.out.println(clzz.getName());
System.out.println(clzz.getCanonicalName());
System.out.println(clzz.getSimpleName());
Above code snippet prints:上面的代码片段打印:
[I
int[]
int[]
[Ljava.lang.Integer;
java.lang.Integer[]
Integer[]
I've been confused by the wide range of different naming schemes as well, and was just about to ask and answer my own question on this when I found this question here.我也对各种不同的命名方案感到困惑,当我在这里发现这个问题时,我正要问和回答我自己的问题。 I think my findings fit it well enough, and complement what's already here.
我认为我的发现非常适合它,并补充了已经在这里的内容。 My focus is looking for documentation on the various terms, and adding some more related terms that might crop up in other places.
我的重点是寻找有关各种术语的文档,并添加一些可能在其他地方出现的更相关的术语。
Consider the following example:考虑以下示例:
package a.b;
class C {
static class D extends C {
}
D d;
D[] ds;
}
The simple name of D
is D
. D
的简单名称是D
。 That's just the part you wrote when declaring the class.这只是你在声明类时写的部分。 Anonymous classes have no simple name.
匿名类没有简单的名称。
Class.getSimpleName()
returns this name or the empty string. Class.getSimpleName()
返回此名称或空字符串。 It is possible for the simple name to contain a $
if you write it like this, since $
is a valid part of an identifier as per JLS section 3.8 (even if it is somewhat discouraged).如果您这样写,简单名称可能包含
$
,因为根据JLS 第 3.8 节, $
是标识符的有效部分(即使有些不鼓励)。
According to the JLS section 6.7 , both abCD
and abCDDD
would be fully qualified names , but only abCD
would be the canonical name of D
.根据JLS 第 6.7 节,
abCD
和abCDDD
都是完全限定名称,但只有abCD
是D
的规范名称。 So every canonical name is a fully qualified name, but the converse is not always true.因此,每个规范名称都是完全限定名称,但反过来并不总是正确的。
Class.getCanonicalName()
will return the canonical name or null
. Class.getCanonicalName()
将返回规范名称或null
。
Class.getName()
is documented to return the binary name , as specified in JLS section 13.1 . Class.getName()
被记录为返回二进制名称,如JLS 第 13.1 节中所述。 In this case it returns abC$D
for D
and [La.bC$D;
在这种情况下,它为
D
和[La.bC$D;
返回abC$D
D; for D[]
.对于
D[]
。
This answer demonstrates that it is possible for two classes loaded by the same class loader to have the same canonical name but distinct binary names .这个答案表明,由同一个类加载器加载的两个类可能具有相同的规范名称但不同的二进制名称。 Neither name is sufficient to reliably deduce the other: if you have the canonical name, you don't know which parts of the name are packages and which are containing classes.
两个名称都不足以可靠地推断出另一个名称:如果您有规范名称,您不知道名称的哪些部分是包,哪些部分包含类。 If you have the binary name, you don't know which
$
were introduced as separators and which were part of some simple name.如果您有二进制名称,您不知道哪些
$
被引入作为分隔符,哪些是一些简单名称的一部分。 (The class file stores the binary name of the class itself and its enclosing class , which allows the runtime to make this distinction .) (类文件存储类本身及其封闭类的二进制名称,这允许运行时进行区分。)
Anonymous classes and local classes have no fully qualified names but still have a binary name . 匿名类和本地类没有完全限定的名称,但仍然有一个二进制名称。 The same holds for classes nested inside such classes.
嵌套在这些类中的类也是如此。 Every class has a binary name.
每个类都有一个二进制名称。
Running javap -v -private
on a/b/C.class
shows that the bytecode refers to the type of d
as La/b/C$D;
在
a/b/C.class
上运行javap -v -private
显示字节码引用d
的类型为La/b/C$D;
and that of the array ds
as [La/b/C$D;
数组
ds
的值为[La/b/C$D;
. . These are called descriptors , and they are specified in JVMS section 4.3 .
这些被称为描述符,它们在JVMS 4.3 节中指定。
The class name a/b/C$D
used in both of these descriptors is what you get by replacing .
这两个描述符中使用的类名
a/b/C$D
是您通过替换.
by /
in the binary name.通过
/
在二进制名称中。 The JVM spec apparently calls this the internal form of the binary name . JVM 规范显然将此称为二进制名称的内部形式。 JVMS section 4.2.1 describes it, and states that the difference from the binary name were for historical reasons.
JVMS 第 4.2.1 节对其进行了描述,并指出与二进制名称的差异是出于历史原因。
The file name of a class in one of the typical filename-based class loaders is what you get if you interpret the /
in the internal form of the binary name as a directory separator, and append the file name extension .class
to it.如果您将二进制名称的内部形式中的
/
解释为目录分隔符,并将文件扩展名.class
附加到它,则在典型的基于文件名的类加载器之一中,类的文件名就是您得到的。 It's resolved relative to the class path used by the class loader in question.它是相对于相关类加载器使用的类路径解析的。
this is best document I found describing getName(), getSimpleName(), getCanonicalName()这是我找到的描述 getName()、getSimpleName()、getCanonicalName() 的最佳文档
https://javahowtodoit.wordpress.com/2014/09/09/java-lang-class-what-is-the-difference-between-class-getname-class-getcanonicalname-and-class-getsimplename/ https://javahowtodoit.wordpress.com/2014/09/09/java-lang-class-what-is-the-difference-between-class-getname-class-getcanonicalname-and-class-getsimplename/
// Primitive type
int.class.getName(); // -> int
int.class.getCanonicalName(); // -> int
int.class.getSimpleName(); // -> int
// Standard class
Integer.class.getName(); // -> java.lang.Integer
Integer.class.getCanonicalName(); // -> java.lang.Integer
Integer.class.getSimpleName(); // -> Integer
// Inner class
Map.Entry.class.getName(); // -> java.util.Map$Entry
Map.Entry.class.getCanonicalName(); // -> java.util.Map.Entry
Map.Entry.class.getSimpleName(); // -> Entry
// Anonymous inner class
Class<?> anonymousInnerClass = new Cloneable() {}.getClass();
anonymousInnerClass.getName(); // -> somepackage.SomeClass$1
anonymousInnerClass.getCanonicalName(); // -> null
anonymousInnerClass.getSimpleName(); // -> // An empty string
// Array of primitives
Class<?> primitiveArrayClass = new int[0].getClass();
primitiveArrayClass.getName(); // -> [I
primitiveArrayClass.getCanonicalName(); // -> int[]
primitiveArrayClass.getSimpleName(); // -> int[]
// Array of objects
Class<?> objectArrayClass = new Integer[0].getClass();
objectArrayClass.getName(); // -> [Ljava.lang.Integer;
objectArrayClass.getCanonicalName(); // -> java.lang.Integer[]
objectArrayClass.getSimpleName(); // -> Integer[]
It is interesting to note that getCanonicalName()
and getSimpleName()
can raise InternalError
when the class name is malformed.有趣的是,当类名格式错误时,
getCanonicalName()
和 getSimpleName( getSimpleName()
会引发InternalError
。 This happens for some non-Java JVM languages, eg, Scala.这发生在一些非 Java JVM 语言上,例如 Scala。
Consider the following (Scala 2.11 on Java 8):考虑以下内容(Java 8 上的 Scala 2.11):
scala> case class C()
defined class C
scala> val c = C()
c: C = C()
scala> c.getClass.getSimpleName
java.lang.InternalError: Malformed class name
at java.lang.Class.getSimpleName(Class.java:1330)
... 32 elided
scala> c.getClass.getCanonicalName
java.lang.InternalError: Malformed class name
at java.lang.Class.getSimpleName(Class.java:1330)
at java.lang.Class.getCanonicalName(Class.java:1399)
... 32 elided
scala> c.getClass.getName
res2: String = C
This can be a problem for mixed language environments or environments that dynamically load bytecode, eg, app servers and other platform software.对于混合语言环境或动态加载字节码的环境,例如应用服务器和其他平台软件,这可能是一个问题。
getName() – returns the name of the entity (class, interface, array class, primitive type, or void) represented by this Class object, as a String. getName() – 以字符串形式返回此 Class 对象表示的实体(类、接口、数组类、原始类型或 void)的名称。
getCanonicalName() – returns the canonical name of the underlying class as defined by the Java Language Specification. getCanonicalName() – 返回 Java 语言规范定义的基础类的规范名称。
getSimpleName() – returns the simple name of the underlying class, that is the name it has been given in the source code. getSimpleName() – 返回底层类的简单名称,即在源代码中给出的名称。
package com.practice;
public class ClassName {
public static void main(String[] args) {
ClassName c = new ClassName();
Class cls = c.getClass();
// returns the canonical name of the underlying class if it exists
System.out.println("Class = " + cls.getCanonicalName()); //Class = com.practice.ClassName
System.out.println("Class = " + cls.getName()); //Class = com.practice.ClassName
System.out.println("Class = " + cls.getSimpleName()); //Class = ClassName
System.out.println("Class = " + Map.Entry.class.getName()); // -> Class = java.util.Map$Entry
System.out.println("Class = " + Map.Entry.class.getCanonicalName()); // -> Class = java.util.Map.Entry
System.out.println("Class = " + Map.Entry.class.getSimpleName()); // -> Class = Entry
}
}
One difference is that if you use an anonymous class you can get a null value when trying to get the name of the class using the getCanonicalName()
一个区别是,如果您使用匿名类,则在尝试使用
getCanonicalName()
获取类的名称时可以获得空值
Another fact is that getName()
method behaves differently than the getCanonicalName()
method for inner classes .另一个事实是
getName()
方法的行为与内部类的getCanonicalName()
方法不同。 getName()
uses a dollar as the separator between the enclosing class canonical name and the inner class simple name. getName()
使用美元作为封闭类规范名称和内部类简单名称之间的分隔符。
To know more about retrieving a class name in Java .要了解有关在 Java 中检索类名的更多信息。
public void printReflectionClassNames(){
StringBuffer buffer = new StringBuffer();
Class clazz= buffer.getClass();
System.out.println("Reflection on String Buffer Class");
System.out.println("Name: "+clazz.getName());
System.out.println("Simple Name: "+clazz.getSimpleName());
System.out.println("Canonical Name: "+clazz.getCanonicalName());
System.out.println("Type Name: "+clazz.getTypeName());
}
outputs:
Reflection on String Buffer Class
Name: java.lang.StringBuffer
Simple Name: StringBuffer
Canonical Name: java.lang.StringBuffer
Type Name: java.lang.StringBuffer
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.