What is the difference between canonical name, simple name and class name in Java Class?
在爪哇,这些区别是什么?
1 2 3 4 | Object o1 = .... o1.getClass().getSimpleName(); o1.getClass().getName(); o1.getClass().getCanonicalName(); |
我已经多次检查了JavaDoc,但这并不能很好地解释它。我还进行了一次测试,但这并没有反映出这些方法被调用的背后的任何真正意义。
如果你对某件事不确定,试着先写一个测试。
我这样做了:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | import java.io.Serializable; import java.util.HashMap; class ClassNameTest { public static void main(String[] arguments) { //primitive System.out.println(int.class.getName()); System.out.println(int.class.getCanonicalName()); System.out.println(int.class.getSimpleName()); System.out.println(int.class.getTypeName()); // Added in Java 8 System.out.println(); //class System.out.println(String.class.getName()); System.out.println(String.class.getCanonicalName()); System.out.println(String.class.getSimpleName()); System.out.println(String.class.getTypeName()); System.out.println(); //inner class System.out.println(HashMap.SimpleEntry.class.getName()); System.out.println(HashMap.SimpleEntry.class.getCanonicalName()); System.out.println(HashMap.SimpleEntry.class.getSimpleName()); System.out.println(HashMap.SimpleEntry.class.getTypeName()); System.out.println(); //anonymous inner class System.out.println(new Serializable(){}.getClass().getName()); System.out.println(new Serializable(){}.getClass().getCanonicalName()); System.out.println(new Serializable(){}.getClass().getSimpleName()); System.out.println(new Serializable(){}.getClass().getTypeName()); } } |
印刷品:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | int int int int java.lang.String java.lang.String String java.lang.String java.util.AbstractMap$SimpleEntry java.util.AbstractMap.SimpleEntry SimpleEntry java.util.AbstractMap$SimpleEntry ClassNameTest$1 null ClassNameTest$4 |
在最后一个块中有一个空行,其中
结果是:
- 该名称是用于动态加载类的名称,例如,使用默认的
ClassLoader 调用Class.forName 。在某个ClassLoader 的范围内,所有类都有唯一的名称。 - 规范名称是将在导入语句中使用的名称。它可能在
toString 或日志操作期间有用。当javac 编译器具有类路径的完整视图时,它通过在编译时冲突完全限定的类和包名称来强制其内部规范名称的唯一性。然而,JVM必须接受这样的名称冲突,因此规范名称不能唯一地标识ClassLoader 中的类。(事后看来,这个吸气剂的更好名称应该是EDOCX1×7;但是这种方法的日期可以追溯到JVM只用于运行Java程序的时候。) - 这个简单的名称松散地标识了类,在
toString 或日志操作期间可能同样有用,但不能保证是唯一的。 - 类型名返回"此类型名称的信息字符串","它类似于toString():它完全是信息性的,没有合同值"(由sir4ur0n编写)
除了Nick Holt的观察,我还对
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | //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()); |
上面的代码段打印:
添加局部类、lambdas和
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 | 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 {} |
这是完整输出:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 | 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; |
所以,这里是规则。首先,让我们从原始类型和
现在,
现在,
这是我找到的描述getname()、getsimplename()、getcanonicalname()的最佳文档
https://javahowtooit.wordpress.com/2014/09/09/java-lang-class-what-is-the-difference-between-class-getname-class-getcanonicalname-and-class-getsimplename/
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | // 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[] |
我也被各种不同的命名方案搞糊涂了,当我在这里发现这个问题时,我正准备就这个问题问和回答我自己的问题。我认为我的发现很好地符合它,并且补充了已经存在的东西。我的重点是寻找各种术语的文档,并添加一些可能在其他地方出现的更相关的术语。
请考虑以下示例:
1 2 3 4 5 6 7 | package a.b; class C { static class D extends C { } D d; D[] ds; } |
D 的简单名称是D 。这就是您在声明类时编写的部分。匿名类没有简单的名称。Class.getSimpleName() 返回此名称或空字符串。如果您这样写的话,简单名称可以包含$ ,因为$ 是JLS第3.8节规定的标识符的有效部分(即使有点不鼓励)。根据JLS第6.7节,
a.b.C.D 和a.b.C.D.D.D 都是完全限定名,但只有a.b.C.D 才是D 的规范名。所以每个规范名称都是完全限定的名称,但converes并不总是正确的。Class.getCanonicalName() 将返回规范名或null 。根据JLS第13.1节的规定,记录
Class.getName() 以返回二进制名称。在这种情况下,它返回a.b.C$D 代表D ,[La.b.C$D; 代表D[] 。这个答案表明,由同一个类装入器装入的两个类可能具有相同的规范名称,但具有不同的二进制名称。两个名称都不足以可靠地推断出另一个:如果您有规范名称,那么您就不知道名称的哪些部分是包,哪些部分包含类。如果您有二进制名称,则不知道哪个
$ 作为分隔符引入,哪些是简单名称的一部分。(类文件存储类本身及其封闭类的二进制名称,这允许运行时进行区分。)匿名类和本地类没有完全限定的名称,但仍然有二进制名称。对于嵌套在此类类中的类也是如此。每个类都有一个二进制名称。
在
a/b/C.class 上运行javap -v -private 表明,字节码将D 的类型称为La/b/C$D; ,数组ds 的类型称为[La/b/C$D; 。这些称为描述符,它们在JVMS第4.3节中指定。在这两个描述符中使用的类名
a/b/C$D 是用二进制名称中的/ 替换. 得到的。显然,jvm规范将其称为二进制名称的内部形式。JVMS第4.2.1节对此进行了描述,并指出与二进制名称的区别是出于历史原因。在一个典型的基于文件名的类加载器中,如果您以二进制名称的内部形式将
/ 解释为目录分隔符,并将文件扩展名.class 附加到该文件中,就会得到类的文件名。它是相对于所讨论的类加载器使用的类路径来解析的。
有趣的是,当类名畸形时,
考虑下面的(Java 8上的Scala 2.11):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | 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 |
对于混合语言环境或动态加载字节码的环境(如应用服务器和其他平台软件),这可能是一个问题。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | 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 |