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

本文翻译自: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. 我也进行了测试,并没有反映这些方法被调用的方式背后的任何真正含义。


#1楼

参考:https://stackoom.com/question/11mzd/Java-Class中的规范名称-简单名称和类名称有什么区别


#2楼

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 default ClassLoader . 名称是您用于动态加载类的名称,例如,使用默认的ClassLoader调用Class.forName Within the scope of a certain ClassLoader , all classes have unique names. 在某个ClassLoader的范围内,所有类都具有唯一的名称。
  • the canonical name is the name that would be used in an import statement. 规范名称是将在import语句中使用的名称。 It might be useful during toString or logging operations. toString或logging操作期间它可能很有用。 When the javac 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 identifies classes within a ClassLoader . 但是,JVM必须接受此类名称冲突,因此规范名称不能唯一标识ClassLoader (In hindsight, a better name for this getter would have been getJavaName ; 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" (as written by sir4ur0n) 类型名称返回“此类型名称的信息字符串”,“它就像toString():它纯粹提供信息,没有合同价值”(由sir4ur0n编写)

#3楼

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[]

#4楼

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): 此外,我添加了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 {}

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

  1. If the class object represents a primitive type or void , all the four methods simply returns its name. 如果类对象表示基本类型或void ,则所有四种方法都只返回其名称。

Now the rules for the getName() method: 现在getName()方法的规则:

  1. Every non-lambda and non-array class or interface (ie, top-level, nested, inner, local and anonymous) has a name (which is returned by 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 ). 每个非lambda和非数组类或接口(即顶级,嵌套,内部,本地和匿名)都有一个名称(由getName()返回),它是包名后跟一个点(如果有的话)是一个包),后跟由编译器生成的类文件的名称(不带后缀.class )。 If there is no package, it is simply the name of the class-file. 如果没有包,它只是类文件的名称。 If the class is an inner, nested, local or anonymous class, the compiler should generate at least one $ in its class-file name. 如果类是内部类,嵌套类,本地类或匿名类,则编译器应在其类文件名中生成至少一个$ Note that for anonymous classes, the class name would end with a dollar-sign followed by a number. 请注意,对于匿名类,类名将以美元符号后跟数字结尾。
  2. Lambda class names are generally unpredictable, and you shouldn't care about they anyway. Lambda类名称通常是不可预测的,无论如何你都不应该关心它们。 Exactly, their name is the name of the enclosing class, followed by $$Lambda$ , followed by a number, followed by a slash, followed by another number. 确切地说,它们的名称是封闭类的名称,后跟$$Lambda$ ,后跟一个数字,后跟斜杠,后跟另一个数字。
  3. The class descriptor of the primitives are 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表示booleanB表示byteS表示shortC表示charI表示intJ表示longF表示floatD表示double For non-array classes and interfaces the class descriptor is L followed by what is given by getName() followed by ; 对于非数组类和接口,类描述符是L后跟getName()后跟的; . For array classes, the class descriptor is [ followed by the class descriptor of the component type (which may be itself another array class). 对于数组类,类描述符是[后跟组件类型的类描述符(可能本身是另一个数组类)。
  4. For array classes, the 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. 这个规则似乎只对组件类型为lambda(可能是一个bug)的数组类失败,但希望这无论如何都不重要,因为即使存在组件类型为lambda的数组类也没有意义。

Now, the toString() method: 现在, toString()方法:

  1. If the class instance represents an interface (or an annotation, which is a special type of interface), the 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() ),它返回"class " + getName()

The getCanonicalName() method: getCanonicalName()方法:

  1. For top-level classes and interfaces, the getCanonicalName() method returns just what the getName() method returns. 对于顶级类和接口, getCanonicalName()方法返回getName()方法返回的内容。
  2. The getCanonicalName() method returns null for anonymous or local classes and for array classes of those. getCanonicalName()方法为匿名或本地类以及这些类的数组类返回null
  3. For inner and nested classes and interfaces, the getCanonicalName() method returns what the getName() method would replacing the compiler-introduced dollar-signs by dots. 对于内部和嵌套类和接口, getCanonicalName()方法返回getName()方法将用点替换编译器引入的美元符号的内容。
  4. For array classes, the 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()方法:

  1. For top-level, nested, inner and local classes, the getSimpleName() returns the name of the class as written in the source file. 对于顶级,嵌套,内部和本地类, getSimpleName()返回在源文件中编写的类的名称。
  2. For anonymous classes the getSimpleName() returns an empty String . 对于匿名类, getSimpleName()返回一个空String
  3. For lambda classes the getSimpleName() just returns what the getName() would return without the package name. 对于lambda类, 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. 这对我来说没有多大意义,看起来像一个bug,但是在lambda类上调用getSimpleName()是没有意义的。
  4. For array classes the 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. 这有一个有趣/奇怪的副作用,即组件类型为匿名类的数组类只有[]作为它们的简单名称。

#5楼

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节 ,无论是abCDabCDDD完全合格的名字 ,但只有abCD会的规范名称 D So every canonical name is a fully qualified name, but the converes 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; 在这种情况下,它返回abC$D D[La.bC$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. by /在二进制名称中。 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. 它是相对于有问题的类加载器使用的类路径解决的。


#6楼

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[]
发布了0 篇原创文章 · 获赞 137 · 访问量 84万+

猜你喜欢

转载自blog.csdn.net/xfxf996/article/details/105343096