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

zqry0prt  于 2023-04-28  发布在  Java
关注(0)|答案(9)|浏览(94)

在Java中,它们之间有什么区别:

Object o1 = ....
o1.getClass().getSimpleName();
o1.getClass().getName();
o1.getClass().getCanonicalName();

我已经检查了Javadoc多次,但这从来没有很好地解释过它。我还运行了一个测试,它没有反映出这些方法调用方式背后的任何真实的含义。

jaxagkaj

jaxagkaj1#

如果你对某些事情不确定,试着先写一个测试。
我这样做了:

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();
    }
}

图纸:

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返回一个空字符串。
看这个的结果是:

*name是动态加载类时使用的名称,例如,调用Class.forName,默认值为ClassLoader。在某个ClassLoader范围内,所有类都有唯一的名称。
*规范名称是将在import语句中使用的名称。它在toString或日志记录操作期间可能很有用。当javac编译器拥有类路径的完整视图时,它通过在编译时冲突完全限定的类名和包名来强制规范名称的唯一性。然而,JVM必须接受这种名称冲突,因此规范名称不能唯一地标识ClassLoader中的类。(事后看来,这个getter的更好的名称应该是getJavaName;但这种方法可以追溯到JVM仅用于运行Java程序的时代。)
*simple name松散地标识类,在toString或日志操作期间也可能有用,但不保证是唯一的。
*type name返回“此类型名称的信息字符串”,“类似于toString:它纯粹是提供信息,没有合同价值”。(作者:sir 4ur 0 n)

此外,您通常可以参考Java语言规范文档,了解这些类型的技术Java API详细信息:

  • 以下是关于此主题的Java 11规范: www.example.com

Example 6.7-2.Example 6.7-2.分别经过Fully Qualified NamesFully Qualified Names v. Canonical Name

yqlxgs2m

yqlxgs2m2#

添加本地类、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开始:
1.如果类对象表示一个基元类型或void,那么所有四个方法都只返回它的名称。
下面是getName()方法的规则:
1.每个非lambda和非数组类或接口(i.例如,顶级,嵌套,内部,本地和匿名)有一个名称(由getName()返回),该名称是包名后跟一个点(如果有包),后跟编译器生成的类文件的名称(没有后缀.class)。如果没有包,它只是类文件的名称。如果类是内部类、嵌套类、本地类或匿名类,编译器应该在类文件名中生成至少一个$。请注意,对于匿名类,类名将以美元符号结尾,后跟一个数字。

  1. Lambda类名通常是不可预测的,无论如何都不应该关心它们。确切地说,它们的名称是封闭类的名称,后跟$$Lambda$,后跟一个数字,后跟一个斜杠,后跟另一个数字。
    1.基元的类描述符是booleanZbyteBshortScharCintIlongJF用于floatD用于double。对于非数组类和接口,类描述符是L,后跟getName(),后跟;。对于数组类,类描述符是[,后跟组件类型的类描述符(它本身可能是另一个数组类)。
    1.对于数组类,getName()方法返回其类描述符。这条规则似乎只对组件类型为lambda的数组类失败(这可能是一个bug),但希望这不重要,因为即使存在组件类型为lambda的数组类也没有意义。
    现在,toString()方法:
    1.如果类示例表示一个接口(或一个注解,这是一种特殊类型的接口),则toString()返回"interface " + getName()。如果它是一个基本类型,它只返回getName()。如果它是其他类型(一个类类型,即使它是一个非常奇怪的类型),它返回"class " + getName()
    getCanonicalName()方法:
    1.对于顶级类和接口,getCanonicalName()方法返回的只是getName()方法返回的内容。
  2. getCanonicalName()方法为匿名类或本地类以及这些类的数组类返回null
    1.对于内部类和嵌套类和接口,getCanonicalName()方法返回getName()方法将用点代替编译器引入的美元符号。
    1.对于数组类,如果组件类型的规范名称是null,则getCanonicalName()方法返回null。否则,它返回组件类型的规范名称,后跟[]
    getSimpleName()方法:
    1.对于顶级类、嵌套类、内部类和局部类,getSimpleName()返回源文件中写入的类名。
    1.对于匿名类,getSimpleName()返回空的String
    1.对于lambda类,getSimpleName()只返回getName()在没有包名的情况下返回的内容。这没有多大意义,对我来说看起来像是一个bug,但是在lambda类上调用getSimpleName()是没有意义的。
    1.对于数组类,getSimpleName()方法返回组件类的简单名称,后跟[]。这有一个有趣/奇怪的副作用,即组件类型为匿名类的数组类只有[]作为其简单名称。
kqlmhetl

kqlmhetl3#

除了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[]
ndasle7k

ndasle7k4#

我也被各种不同的命名方案弄糊涂了,当我在这里发现这个问题时,我正要问和回答我自己的问题。我认为我的发现很适合它,并补充了已经存在的东西。我的重点是寻找各种术语的 * 文档 *,并添加一些可能在其他地方出现的更多相关术语。
考虑以下示例:

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.Da.b.C.D.D.D都是全限定名称,但只有a.b.C.DD规范名称。因此,每个规范名称都是完全限定名称,但匡威并不总是正确的。Class.getCanonicalName()将返回规范名称或null
  • Class.getName()被记录为返回二进制名称,如JLS第13节所指定。1.在这种情况下,它返回Da.b.C$DD[][La.b.C$D;
  • This answer演示了由同一个类加载器加载的两个类可能具有相同的规范名称但不同的二进制名称。两个名字都不足以可靠地推断出另一个:如果你有规范名称,你就不知道名称的哪些部分是包,哪些部分包含类。如果您有二进制名称,您不知道哪些$是作为分隔符引入的,哪些是某个简单名称的一部分。(类文件存储类本身及其封闭类的二进制名称,这允许运行库进行这种区分。)
  • 匿名类和局部类没有完全限定名,但仍有二进制名。这同样适用于嵌套在此类类中的类。* 每个 * 类都有一个二进制名称。
  • 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附加到它,则会得到类的文件名。它是相对于有问题的类装入器所使用的类路径进行解析的。
cyvaqqii

cyvaqqii5#

这是我找到的描述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[]
xjreopfe

xjreopfe6#

有趣的是,当类名格式不正确时,getCanonicalName()getSimpleName()可以引发InternalError。这种情况发生在一些非Java JVM语言中,例如。Scala.
考虑以下情况(Scala 2.Java 8):

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

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

tnkciper

tnkciper7#

getName()-返回此Class对象表示的实体(类、接口、数组类、原始类型或void)的名称,作为String。
getCanonicalName()-返回Java语言规范定义的底层类的规范名称。
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 
  }
}

一个区别是,如果您使用 * 匿名类 *,则在尝试使用getCanonicalName()获取类名时可能会获得空值。
另一个事实是getName()方法的行为与getCanonicalName()方法的行为不同。getName()使用美元作为封闭类规范名称和内部类简单名称之间的分隔符。
了解更多关于retrieving a class name in Java

to94eoyn

to94eoyn8#

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
yfwxisqw

yfwxisqw9#

Java类中的规范名、简单名和类名有什么区别?
Java中的方法getClass()用于获取特定对象示例关联的Class对象Class对象代表对象所属的信息。
getSimpleName()getName()getCanonicalName()方法在以下方面有所不同:

  • getSimpleName()是返回Class对象所表示的类的简单名称的方法。它没有关于包的信息。
    **例如:**如果objectOnejava.util.ArrayList class的示例,objectOne.getClass().getSimpleName()将返回ArrayList
  • getName()是返回Class对象表示的类的完全限定名的方法,包括包名。
    **例如:**如果objectOnejava.util.ArrayList class的示例,objectOne.getClass().getName()将返回java.util.ArrayList
  • getCanonicalName()是返回Class对象表示的类的规范名称的方法。规范名称是类的完全限定名称,但删除了所有泛型类型信息。
    **例如:**如果objectOnejava.util.ArrayList类的一个示例,带有generic type parameter of String,则o1.getClass().getCanonicalName()将返回java.util.ArrayList

相关问题