Java Class中的规范名称,简单名称和类名称有什么区别?

Moh*_*aie 921 java

在Java中,这些之间有什么区别:

Object o1 = ....
o1.getClass().getSimpleName();
o1.getClass().getName();
o1.getClass().getCanonicalName();
Run Code Online (Sandbox Code Playgroud)

我多次检查过Javadoc,但这从未解释过.我也进行了测试,并没有反映这些方法被调用的方式背后的任何真正含义.

Nic*_*olt 1076

如果您对某些事情不确定,请先尝试编写测试.

我这样做了:

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();
    }
}
Run Code Online (Sandbox Code Playgroud)

打印:

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

最后一个块中有一个空行,getSimpleName返回一个空字符串.

看到这个的结果是:

  • 名称是您愿意使用动态加载与类,例如,调用的名称Class.forName与默认ClassLoader.
  • 规范名称是将在import语句中使用的名称和唯一标识类.在ClassLoader操作期间或记录操作时可能很有用.
  • 简单的名字松散标识类,又可能是有用的过程toString或记录的操作,但不能保证是唯一的.

  • Java 8还添加了getTypeName()...关心更新吗? (18认同)
  • 你认为需要多少额外的东西? (5认同)
  • 然而,IT会很疯狂,这种假设会让恶意行为者发挥作用.有人说"哦,我们知道课程永远不会从小写/包开始永远不会从大写字母开始".当然,有权访问您的类加载器的恶意行为者已经可以做出可怕的事情,所以这可能不是一个绝对可怕的假设. (3认同)
  • @AnupamSaini是的.在真实应用程序中拥有这样的包名称将是疯狂的. (2认同)
  • @PieterDeBie怎么样?您需要知道的只是您要测试的方法名称. (2认同)
  • 查看jdk8_102(标准Oracle JDK)中的实现,似乎只是“ getName()”,不同之处在于,数组不是像[[[[Ljava.lang.Object;]那样表示,而是像java.lang那样表示。 .Object [] [] []`。 (2认同)

Vic*_*usa 83

添加本地类,lambda和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 {}
Run Code Online (Sandbox Code Playgroud)

这是完整的输出:

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;
Run Code Online (Sandbox Code Playgroud)

所以,这是规则.首先,让我们从原始类型开始void:

  1. 如果类对象表示基本类型void,则所有四种方法都只返回其名称.

现在该getName()方法的规则:

  1. 每个非lambda和非数组类或接口(即顶级,嵌套,内部,本地和匿名)都有一个名称(由其返回getName()),即包名称后跟一个点(如果有一个包) ),后跟由编译器生成的类文件的名称(不带后缀.class).如果没有包,它只是类文件的名称.如果类是内部类,嵌套类,本地类或匿名类,则编译器应$在其类文件名中生成至少一个类.请注意,对于匿名类,类名将以美元符号后跟数字结尾.
  2. Lambda类名称通常是不可预测的,无论如何你都不应该关心它们.确切地说,它们的名称是封闭类的名称$$Lambda$,后跟一个数字,后跟一个斜杠,后跟另一个数字.
  3. 原语的类描述符是Zboolean,Bbyte,Sshort,Cchar,Iint,Jlong,Ffloat,并Ddouble.对于非数组类和接口,类描述符L后跟后面给出getName()的内容;.对于数组类,类描述符[后跟组件类型的类描述符(可能本身是另一个数组类).
  4. 对于数组类,该getName()方法返回其类描述符.这个规则似乎只对组件类型为lambda(可能是一个bug)的数组类失败,但希望这无论如何都不重要,因为即使存在组件类型为lambda的数组类也没有意义.

现在,toString()方法:

  1. 如果类实例表示接口(或注释,这是一种特殊类型的接口),则toString()返回"interface " + getName().如果它是原始的,它只返回getName().如果它是其他东西(类类型,即使它是一个非常奇怪的类),它返回"class " + getName().

getCanonicalName()方法:

  1. 对于顶级类和接口,该getCanonicalName()方法返回该getName()方法返回的内容.
  2. getCanonicalName()方法返回null匿名或本地类以及这些类的数组类.
  3. 对于内部和嵌套类和接口,该getCanonicalName()方法返回该getName()方法将用点替换编译器引入的美元符号的方法.
  4. 对于数组类,如果组件类型的规范名称为,则该getCanonicalName()方法返回.否则,它返回组件类型的规范名称,后跟.nullnull[]

getSimpleName()方法:

  1. 对于顶级,嵌套,内部和本地类,getSimpleName()返回源文件中编写的类的名称.
  2. 对于匿名类,getSimpleName()返回空String.
  3. 对于lambda类,getSimpleName()just返回getName()没有包名返回的内容.这对我来说没有多大意义,看起来像是一个bug,但是在调用getSimpleName()lambda类时没有任何意义.
  4. 对于数组类,该getSimpleName()方法返回组件类的简单名称,后跟[].这有一个有趣/奇怪的副作用,即组件类型为匿名类的数组类[]就像它们的简单名称一样.

  • “…用点代替美元符号”:仅替换定界符中引入的美元符号。您可以将美元作为简单名称的一部分,而这些将保持不变。 (2认同)

ger*_*rdw 79

除了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());
Run Code Online (Sandbox Code Playgroud)

上面的代码片段打印:

[I
int[]
int[]

[Ljava.lang.Integer;
java.lang.Integer[]
Integer[]
Run Code Online (Sandbox Code Playgroud)

  • 我建议编辑上面的答案会好得多. (27认同)

MvG*_*MvG 14

我也对各种不同的命名方案感到困惑,当我在这里找到这个问题时,我正要问这个问题并回答我自己的问题.我认为我的发现很适合它,并补充已经存在的内容.我的重点是寻找各种术语的文档,并添加一些可能在其他地方出现的更多相关术语.

请考虑以下示例:

package a.b;
class C {
  static class D extends C {
  }
  D d;
  D[] ds;
}
Run Code Online (Sandbox Code Playgroud)
  • 简单的名字DD.这只是你在宣布课时写的部分.匿名类没有简单的名称.Class.getSimpleName()返回此名称或空字符串.$如果您这样写,则简单名称可能包含a ,因为它$是标识符的有效部分.

  • 根据JLS的第6.7节,都a.b.C.Da.b.C.D.D.D完全合格的名称,而只是a.b.C.D将是规范名称D.因此,每个规范名称都是完全限定的名称,但并不总是如此.Class.getCanonicalName()将返回规范名称或null.

  • Class.getName()记录为返回二进制名称,如JLS第13.1节中所述.在这种情况下,它返回a.b.C$Dfor D[La.b.C$D;for D[].

  • 这个答案表明,由同一个类加载器加载的两个类可能具有相同的规范名称但不同的二进制名称.这两个名称都不足以可靠地推断出另一个名称:如果您具有规范名称,则不知道名称的哪些部分是包,哪些部分包含类.如果你有二进制名称,你不知道哪些$是作为分隔符引入的,哪些是一些简单名称的一部分.

  • 匿名类本地类没有完全限定的名称,但仍具有二进制名称.嵌套在这些类中的类也是如此.每个类都有一个二进制名称.

  • 运行javap -v -privatea/b/C.class显示,字节码指的类型dLa/b/C$D;与该阵列的ds作为[La/b/C$D;.这些被称为描述符,它们在JVMS第4.3节中指定.

  • a/b/C$D两个描述符中使用的类名是.通过/二进制名替换的结果.JVM规范显然将其称为二进制名称内部形式.JVMS第4.2.1节描述了它,并声明与二进制名称的差异是出于历史原因.

  • 文件名在典型基于文件名的类加载器的一个一类的是,如果你解释你所得到的/二进制名称作为目录分隔符的内部形式,文件扩展名追加.class到它.它是相对于有问题的类加载器使用的类路径解决的.

  • 这是应该接受的答案,因为它是引用JLS并使用适当术语的唯一答案。 (2认同)

小智 10

这是我发现描述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/

// 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[]
Run Code Online (Sandbox Code Playgroud)


Sim*_*Sim 5

有趣的是,当类名格式错误时,getCanonicalName()getSimpleName()会引发异常。InternalError对于某些非 Java JVM 语言(例如 Scala),会发生这种情况。

请考虑以下事项(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
Run Code Online (Sandbox Code Playgroud)

对于混合语言环境或动态加载字节码的环境(例如应用程序服务器和其他平台软件),这可能是一个问题。