This answer演示了由同一个类加载器加载的两个类可能具有相同的规范名称但不同的二进制名称。两个名字都不足以可靠地推断出另一个:如果你有规范名称,你就不知道名称的哪些部分是包,哪些部分包含类。如果您有二进制名称,您不知道哪些$是作为分隔符引入的,哪些是某个简单名称的一部分。(类文件存储类本身及其封闭类的二进制名称,这允许运行库进行这种区分。)
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
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
getCanonicalName()是返回Class对象表示的类的规范名称的方法。规范名称是类的完全限定名称,但删除了所有泛型类型信息。 **例如:**如果objectOne是java.util.ArrayList类的一个示例,带有generic type parameter of String,则o1.getClass().getCanonicalName()将返回java.util.ArrayList。
9条答案
按热度按时间jaxagkaj1#
如果你对某些事情不确定,试着先写一个测试。
我这样做了:
图纸:
最后一个块中有一个空条目,
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详细信息:
Example 6.7-2.
和Example 6.7-2.
分别经过Fully Qualified Names
和Fully Qualified Names v. Canonical Name
yqlxgs2m2#
添加本地类、lambdas和
toString()
方法来完成前两个答案。此外,我添加了lambdas数组和匿名类数组(虽然在实践中没有任何意义):这是完整的输出:
规则是这样的首先,让我们从原始类型和
void
开始:1.如果类对象表示一个基元类型或
void
,那么所有四个方法都只返回它的名称。下面是
getName()
方法的规则:1.每个非lambda和非数组类或接口(i.例如,顶级,嵌套,内部,本地和匿名)有一个名称(由
getName()
返回),该名称是包名后跟一个点(如果有包),后跟编译器生成的类文件的名称(没有后缀.class
)。如果没有包,它只是类文件的名称。如果类是内部类、嵌套类、本地类或匿名类,编译器应该在类文件名中生成至少一个$
。请注意,对于匿名类,类名将以美元符号结尾,后跟一个数字。$$Lambda$
,后跟一个数字,后跟一个斜杠,后跟另一个数字。1.基元的类描述符是
boolean
的Z
,byte
的B
,short
的S
,char
的C
,int
的I
,long
的J
,F
用于float
,D
用于double
。对于非数组类和接口,类描述符是L
,后跟getName()
,后跟;
。对于数组类,类描述符是[
,后跟组件类型的类描述符(它本身可能是另一个数组类)。1.对于数组类,
getName()
方法返回其类描述符。这条规则似乎只对组件类型为lambda的数组类失败(这可能是一个bug),但希望这不重要,因为即使存在组件类型为lambda的数组类也没有意义。现在,
toString()
方法:1.如果类示例表示一个接口(或一个注解,这是一种特殊类型的接口),则
toString()
返回"interface " + getName()
。如果它是一个基本类型,它只返回getName()
。如果它是其他类型(一个类类型,即使它是一个非常奇怪的类型),它返回"class " + getName()
。getCanonicalName()
方法:1.对于顶级类和接口,
getCanonicalName()
方法返回的只是getName()
方法返回的内容。getCanonicalName()
方法为匿名类或本地类以及这些类的数组类返回null
。1.对于内部类和嵌套类和接口,
getCanonicalName()
方法返回getName()
方法将用点代替编译器引入的美元符号。1.对于数组类,如果组件类型的规范名称是
null
,则getCanonicalName()
方法返回null
。否则,它返回组件类型的规范名称,后跟[]
。getSimpleName()
方法:1.对于顶级类、嵌套类、内部类和局部类,
getSimpleName()
返回源文件中写入的类名。1.对于匿名类,
getSimpleName()
返回空的String
。1.对于lambda类,
getSimpleName()
只返回getName()
在没有包名的情况下返回的内容。这没有多大意义,对我来说看起来像是一个bug,但是在lambda类上调用getSimpleName()
是没有意义的。1.对于数组类,
getSimpleName()
方法返回组件类的简单名称,后跟[]
。这有一个有趣/奇怪的副作用,即组件类型为匿名类的数组类只有[]
作为其简单名称。kqlmhetl3#
除了Nick Holt的观察之外,我还运行了几个
Array
数据类型的案例:上面的代码片段打印:
ndasle7k4#
我也被各种不同的命名方案弄糊涂了,当我在这里发现这个问题时,我正要问和回答我自己的问题。我认为我的发现很适合它,并补充了已经存在的东西。我的重点是寻找各种术语的 * 文档 *,并添加一些可能在其他地方出现的更多相关术语。
考虑以下示例:
D
的简单名称为D
。这只是声明类时编写的部分。匿名类没有简单的名字。Class.getSimpleName()
返回此名称或空字符串。如果您这样写,简单名称可能包含$
,因为$
是JLS第3节中标识符的有效部分。8(即使有点沮丧)。a.b.C.D
和a.b.C.D.D.D
都是全限定名称,但只有a.b.C.D
是D
的规范名称。因此,每个规范名称都是完全限定名称,但匡威并不总是正确的。Class.getCanonicalName()
将返回规范名称或null
。Class.getName()
被记录为返回二进制名称,如JLS第13节所指定。1.在这种情况下,它返回D
的a.b.C$D
和D[]
的[La.b.C$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
附加到它,则会得到类的文件名。它是相对于有问题的类装入器所使用的类路径进行解析的。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/
xjreopfe6#
有趣的是,当类名格式不正确时,
getCanonicalName()
和getSimpleName()
可以引发InternalError
。这种情况发生在一些非Java JVM语言中,例如。Scala.考虑以下情况(Scala 2.Java 8):
这对于混合语言环境或动态加载字节码的环境来说可能是个问题,例如例如,应用服务器和其他平台软件。
tnkciper7#
getName()-返回此Class对象表示的实体(类、接口、数组类、原始类型或void)的名称,作为String。
getCanonicalName()-返回Java语言规范定义的底层类的规范名称。
getSimpleName()-返回底层类的简单名称,即源代码中给出的名称。
一个区别是,如果您使用 * 匿名类 *,则在尝试使用
getCanonicalName()
获取类名时可能会获得空值。另一个事实是
getName()
方法的行为与getCanonicalName()
方法的行为不同。getName()
使用美元作为封闭类规范名称和内部类简单名称之间的分隔符。了解更多关于retrieving a class name in Java
to94eoyn8#
yfwxisqw9#
Java类中的规范名、简单名和类名有什么区别?
Java
中的方法getClass()
用于获取特定对象示例关联的Class对象。Class对象代表对象所属类的信息。getSimpleName()
、getName()
和getCanonicalName()
方法在以下方面有所不同:getSimpleName()
是返回Class对象所表示的类的简单名称的方法。它没有关于包的信息。**例如:**如果
objectOne
是java.util.ArrayList class
的示例,objectOne.getClass().getSimpleName()
将返回ArrayList
。getName()
是返回Class对象表示的类的完全限定名的方法,包括包名。**例如:**如果
objectOne
是java.util.ArrayList class
的示例,objectOne.getClass().getName()
将返回java.util.ArrayList
。getCanonicalName()
是返回Class对象表示的类的规范名称的方法。规范名称是类的完全限定名称,但删除了所有泛型类型信息。**例如:**如果
objectOne
是java.util.ArrayList
类的一个示例,带有generic type parameter of String
,则o1.getClass().getCanonicalName()
将返回java.util.ArrayList
。