在 Java 中,这些之间有什么区别:
Object o1 = ....
o1.getClass().getSimpleName();
o1.getClass().getName();
o1.getClass().getCanonicalName();
我多次检查过 Javadoc,但这从未解释过。我也进行了测试,并没有反映这些方法被调用的方式背后的任何真正含义。
如果您对某些事情不确定,请先尝试编写测试。
我这样做了:
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());
}
}
打印:
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
最后一个块中有一个空行,其中getSimpleName
返回一个空字符串。
看到这个的结果是:
ClassLoader
调用Class.forName
。在某个ClassLoader
的范围内,所有类都具有唯一的名称。 toString
或 logging 操作期间它可能很有用。当javac
编译器具有类路径的完整视图时,它通过在编译时碰撞完全限定的类和包名称来强制其中的规范名称的唯一性。但是,JVM 必须接受此类名称冲突,因此规范名称不能唯一标识ClassLoader
。 (事后看来,这个 getter 的一个更好的名字就是getJavaName
; 但是这个方法可以追溯到 JVM 仅用于运行 Java 程序的时候。) toString
或日志记录操作期间可能也很有用,但不保证是唯一的。 除了 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());
上面的代码片段打印:
[I
int[]
int[]
[Ljava.lang.Integer;
java.lang.Integer[]
Integer[]
添加本地类,lambdas 和toString()
方法来完成前两个答案。此外,我添加了 lambdas 数组和匿名类数组(虽然在实践中没有任何意义):
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 {}
这是完整的输出:
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;
所以,这是规则。首先,让我们从原始类型和void
:
void
,则所有四种方法都只返回其名称。 现在getName()
方法的规则:
getName()
返回),它是包名后跟一个点(如果有的话)是一个包),后跟由编译器生成的类文件的名称(不带后缀.class
)。如果没有包,它只是类文件的名称。如果类是内部类,嵌套类,本地类或匿名类,则编译器应在其类文件名中生成至少一个$
。请注意,对于匿名类,类名将以美元符号后跟数字结尾。 $$Lambda$
,后跟一个数字,后跟斜杠,后跟另一个数字。 Z
表示boolean
, B
表示byte
, S
表示short
, C
表示char
, I
表示int
, J
表示long
, F
表示float
, D
表示double
。对于非数组类和接口,类描述符是L
后跟getName()
后跟的;
。对于数组类,类描述符是[
后跟组件类型的类描述符(可能本身是另一个数组类)。 getName()
方法返回其类描述符。这个规则似乎只对组件类型为 lambda(可能是一个 bug)的数组类失败,但希望这无论如何都不重要,因为即使存在组件类型为 lambda 的数组类也没有意义。 现在, toString()
方法:
toString()
返回"interface " + getName()
。如果它是原始的,它只返回getName()
。如果它是其他东西(类类型,即使它是一个非常奇怪的"class " + getName()
),它返回"class " + getName()
。 getCanonicalName()
方法:
getCanonicalName()
方法返回getName()
方法返回的内容。 getCanonicalName()
方法为匿名或本地类以及这些类的数组类返回null
。 getCanonicalName()
方法返回getName()
方法将用点替换编译器引入的美元符号的内容。 null
,则getCanonicalName()
方法返回null
。否则,它返回组件类型的规范名称,后跟[]
。 getSimpleName()
方法:
getSimpleName()
返回在源文件中编写的类的名称。 getSimpleName()
返回一个空String
。 getSimpleName()
只返回getName()
在没有包名的情况下返回的内容。这对我来说没什么意义,看起来像个 bug,但是在 lambda 类上调用getSimpleName()
是没有意义的。 getSimpleName()
方法返回组件类的简单名称,后跟[]
。这有一个有趣 / 奇怪的副作用,即组件类型为匿名类的数组类只有[]
作为它们的简单名称。