在Java中,每个类都有一个名称。 类位于软件包中,这使我们程序员可以一起工作,避免名称冲突。 我可以为A
类命名,也可以为A
类命名,只要它们位于不同的程序包中,它们就可以很好地协同工作。
如果您查看Class
的API,您肯定会注意到有三种不同的方法为您提供类的名称:
-
getSimpleName()
为您提供不带包的类的名称。 -
getName()
为您提供类的名称,其全包名称位于前面。 -
getCanonicalName()
为您提供类的规范名称。
简单吗? 好吧,第一个很简单,第二个也很有意义,除非存在令人不安的规范名称。 这还不是很明显。 而且,如果您不知道规范名称是什么,您可能还会在第二时间受到Java技能的干扰。 两者有什么区别?
如果您想要精确的解释,请访问Java语言规范6.7章 。 在这里,我们进行一些更简单,更简单的理解,尽管不够全面。
让我们看一些例子:
package pakage.subpackage.evensubberpackage;
import org.junit.Assert;
import org.junit.Test;
public class WhatIsMyName {
@Test
public void classHasName() {
final Class<?> klass = WhatIsMyName.class;
final String simpleNameExpected = "WhatIsMyName";
Assert.assertEquals(simpleNameExpected, klass.getSimpleName());
final String nameExpected = "pakage.subpackage.evensubberpackage.WhatIsMyName";
Assert.assertEquals(nameExpected, klass.getName());
Assert.assertEquals(nameExpected, klass.getCanonicalName());
}
...
这种“单元测试”运行得很好。 但是正如您所看到的,在这种情况下,名称和规范名称没有区别。 (请注意,程序包的名称是pakage
而不是package
。要测试您的Java词汇技巧,请回答为什么?)
让我们看一下来自同一junit测试文件的下一个示例:
@Test
public void arrayHasName() {
final Class<?> klass = WhatIsMyName[].class;
final String simpleNameExpected = "WhatIsMyName[]";
Assert.assertEquals(simpleNameExpected, klass.getSimpleName());
final String nameExpected = "[Lpakage.subpackage.evensubberpackage.WhatIsMyName;";
Assert.assertEquals(nameExpected, klass.getName());
final String canonicalNameExpected = "pakage.subpackage.evensubberpackage.WhatIsMyName[]";
Assert.assertEquals(canonicalNameExpected, klass.getCanonicalName());
}
现在有区别。 当我们谈论数组时,简单名称会发出信号,即在其后面加上了括号,就像在Java源代码中所做的那样。 “普通”名称看起来有点怪异。 它以L
开头并附加分号。 这反映了JVM中类名的内部表示。 规范名称的更改类似于简单名称:对于以前的类,其类具有所有包名称作为前缀并附加了方括号。 似乎getName()
更像是类的JVM名称,而getCanonicalName()
更像是Java源代码级别的完全限定名称。
让我们继续其他示例(我们仍在同一文件中):
class NestedClass{}
@Test
public void nestedClassHasName() {
final Class<?> klass = NestedClass.class;
final String simpleNameExpected = "NestedClass";
Assert.assertEquals(simpleNameExpected, klass.getSimpleName());
final String nameExpected = "pakage.subpackage.evensubberpackage.WhatIsMyName$NestedClass";
Assert.assertEquals(nameExpected, klass.getName());
final String canonicalNameExpected = "pakage.subpackage.evensubberpackage.WhatIsMyName.NestedClass";
Assert.assertEquals(canonicalNameExpected, klass.getCanonicalName());
}
区别在于班级名称中的美元符号。 同样,“名称”更多是JVM使用的名称,而规范名称是Java源代码之类的名称。 如果编译此代码,则Java编译器将生成以下文件:
-
WhatIsMyName.class
和 -
WhatIsMyName$NestedClass.class
即使该类被命名为嵌套类,它实际上也是一个内部类。 但是在命名上没有什么区别:另一个类中的静态或非静态类仅被命名为相同。 现在,让我们看一些更有趣的东西:
@Test
public void methodClassHasName() {
class MethodClass{};
final Class<?> klass = MethodClass.class;
final String simpleNameExpected = "MethodClass";
Assert.assertEquals(simpleNameExpected, klass.getSimpleName());
final String nameExpected = "pakage.subpackage.evensubberpackage.WhatIsMyName$1MethodClass";
Assert.assertEquals(nameExpected, klass.getName());
final String canonicalNameExpected = null;
Assert.assertEquals(canonicalNameExpected, klass.getCanonicalName());
}
这次我们在方法内部有一个类。 这不是通常的情况,但是从Java语言的角度来看是有效的。 类的简单名称就是这样:类的简单名称。 没什么奇怪的。
但是,“正常”名称很有趣。 Java编译器为该类生成一个JVM名称,该名称中包含一个数字。 为什么? 因为没有什么可以阻止我在测试类的另一个方法中拥有一个具有相同名称的类,并插入数字是防止JVM名称冲突的方法。 JVM不了解或不关心内部和嵌套类或方法内部定义的类。 一类就是一类。 如果编译代码,则可能会看到javac生成的文件WhatIsMyName$1MethodClass.class
。 我不得不添加“可能”不是因为我认为您可能会盲目,而是因为该名称实际上是Java编译器的内部问题。 它可能会选择其他避免名称冲突的策略,尽管我不知道有什么不同于上面的编译器。
规范名称是最有趣的。 它不存在! 它为空。 为什么? 因为您不能从定义它的方法之外访问此类。 它没有规范名称。 我们继续。
匿名类呢? 他们不应该有名字。 毕竟,这就是为什么它们被称为匿名的原因。
@Test
public void anonymousClassHasName() {
final Class<?> klass = new Object(){}.getClass();
final String simpleNameExpected = "";
Assert.assertEquals(simpleNameExpected, klass.getSimpleName());
final String nameExpected = "pakage.subpackage.evensubberpackage.WhatIsMyName$1";
Assert.assertEquals(nameExpected, klass.getName());
final String canonicalNameExpected = null;
Assert.assertEquals(canonicalNameExpected, klass.getCanonicalName());
}
实际上,它们没有简单的名称。 简单名称是空字符串。 它们确实具有名称,但由编译器组成。 较差的javac没有其他选择。 它甚至必须为未命名的类组成一些名称。 它必须为JVM生成代码,并且必须将其保存到某个文件中。 规范名称再次为null。
我们已经准备好示例了吗? 否。最后我们有一些简单的东西(又称原始)。 Java基元。
@Test
public void intClassHasName() {
final Class<?> klass = int.class;
final String intNameExpected = "int";
Assert.assertEquals(intNameExpected, klass.getSimpleName());
Assert.assertEquals(intNameExpected, klass.getName());
Assert.assertEquals(intNameExpected, klass.getCanonicalName());
}
如果该类表示一个原语,例如int
(还有什么比int更简单?),则简单名称,“ the”名称和规范名称都全部是该原语的int
名称。
就像原始数组一样非常简单吗?
@Test
public void intArrayClassHasName() {
final Class<?> klass = int[].class;
final String simpleNameExpected = "int[]";
Assert.assertEquals(simpleNameExpected, klass.getSimpleName());
final String nameExpected = "[I";
Assert.assertEquals(nameExpected, klass.getName());
final String canonicalNameExpected = "int[]";
Assert.assertEquals(canonicalNameExpected, klass.getCanonicalName());
}
好吧,这并不简单。 名称为[I
,这有点神秘,除非您阅读JVM规范的相应章节 。 也许我再说一次。
结论
类的简单名称很简单。 对于JVM级别的事情, getName()
返回的“名称”是一个有趣的东西。 getCanonicalName()
是最像Java源代码的那个。
- 您可以从GitHub的g789 e789d700d3c9abc6afa0获得上述示例的完整源代码。
翻译自: https://www.javacodegeeks.com/2014/09/name-of-the-class.html