Java类文件可以使用保留的关键字作为名称吗?

我知道,Java编译语言不是Java-byte-code-format-for-JVM-execution。 在.class格式中有一些有效的例子,但不在.java源代码中,例如无构造函数的类和合成方法。

  1. 如果我们用一个保留的Java语言关键字(例如intwhile手工制作一个.class文件作为类,方法或字段名称,Java虚拟机是否会接受它加载?

  2. 如果加载类,是否意味着访问此类或成员的唯一方法是通过Javareflection,因为Java编程语言中的名称在语法上是非法的?

在字节码级别对类名唯一的限制是它们不能包含字符[.; 而且它们最长不超过65535字节。 除此之外,这意味着您可以自由使用保留字,空格,特殊字符,Unicode,甚至是像换行符这样的奇怪的东西。

理论上,甚至可以在类名中使用空字符,但由于在文件名中不能包含空字符,所以不能在jar中包含这样的类文件。 您可能可以创build并加载一个虽然。

下面是你可以做的一些事情的例子(用Krakatau汇编):

 ; Entry point for the jar .class Main .super java/lang/Object .method public static main : ([Ljava/lang/String;)V .limit stack 10 .limit locals 10 invokestatic int hello ()V invokestatic "-42" hello ()V invokestatic "" hello ()V invokestatic " some whitespace and \t tabs" hello ()V invokestatic "new\nline" hello ()V invokestatic 'name with "Quotes" in it' hello ()V return .end method .end class .class int .super java/lang/Object .method public static hello : ()V .limit stack 2 .limit locals 0 getstatic java/lang/System out Ljava/io/PrintStream; ldc "Hello from int" invokevirtual java/io/PrintStream println (Ljava/lang/Object;)V return .end method .end class .class "-42" .super java/lang/Object .method public static hello : ()V .limit stack 2 .limit locals 0 getstatic java/lang/System out Ljava/io/PrintStream; ldc "Hello from -42" invokevirtual java/io/PrintStream println (Ljava/lang/Object;)V return .end method .end class ; Even the empty string can be a class name! .class "" .super java/lang/Object .method public static hello : ()V .limit stack 2 .limit locals 0 getstatic java/lang/System out Ljava/io/PrintStream; ldc "Hello from " invokevirtual java/io/PrintStream println (Ljava/lang/Object;)V return .end method .end class .class " some whitespace and \t tabs" .super java/lang/Object .method public static hello : ()V .limit stack 2 .limit locals 0 getstatic java/lang/System out Ljava/io/PrintStream; ldc "Hello from some whitespace and \t tabs" invokevirtual java/io/PrintStream println (Ljava/lang/Object;)V return .end method .end class .class "new\nline" .super java/lang/Object .method public static hello : ()V .limit stack 2 .limit locals 0 getstatic java/lang/System out Ljava/io/PrintStream; ldc "Hello from new\nline" invokevirtual java/io/PrintStream println (Ljava/lang/Object;)V return .end method .end class .class 'name with "Quotes" in it' .super java/lang/Object .method public static hello : ()V .limit stack 2 .limit locals 0 getstatic java/lang/System out Ljava/io/PrintStream; ldc "Hello from name with \"Quotes\" in it" invokevirtual java/io/PrintStream println (Ljava/lang/Object;)V return .end method .end class 

执行输出:

 Hello from int Hello from -42 Hello from Hello from some whitespace and tabs Hello from new line Hello from name with "Quotes" in it 

请参阅Holger对JVM规范中规则确切引用的回答 。

是的,你可以使用保留字。 这些词只是为了编译器。 它们不会出现在生成的字节码中。

使用保留的Java语言的一个例子是基于JVM的Scala语言。 Scala具有与Java不同的构造和语法,但编译为Java字节代码,以便在JVM上运行。

这是合法的Scala:

 class `class` 

这定义了一个名为class带有一个无参数的构造函数。 在已编译的class.class文件上运行javap (反汇编)

 public class class { public class(); } 

Scala可以做任何其他的Java保留字。

 class int class `while` class goto 

它们也可以用于方法或字段名称。

正如你所猜测的那样,除了reflection之外,你将无法使用Java中的这些类。 你可以使用类似“自定义”的类文件,例如从Scala编译器生成的类文件。

总之,这是javac(编译器)的限制,而不是java(VM /运行时环境)。

有关名称的限制在JVM规范中是固定的:

§4.2.1。 二进制类和接口名称

出现在类文件结构中的类和接口名称始终以称为二进制名称(JLS§13.1)的完全限定forms表示。 这样的名字总是表示为CONSTANT_Utf8_info结构(§4.4.7),因此可以从整个Unicode代码空间中绘制出来,不受进一步限制。

由于历史原因,出现在类文件结构中的二进制名称的语法不同于JLS§13.1中记载的二进制名称的语法。 在这种内部格式中,通常将组成二进制名称的标识符的ASCII句点( . )replace为ASCII正斜杠( / )。 标识符本身必须是非限定名称(§4.2.2)。

§4.2.2。 不合格的名字

方法,字段,局部variables和forms参数的名称存储为非限定名称。 非限定名称必须包含至less一个Unicode代码点,且不得包含任何ASCII字符. ; [ / (也就是句号或分号或左方括号或正斜杠)。

方法名被进一步限制,除了特殊的方法名<init><clinit> (§2.9)之外,它们不能包含ASCII字符<> (即左尖括号或右尖括号) 。

所以答案是,在二进制级别上只能使用几个字符。 首先, /是包裹分隔符。 那么, 和[不能使用,因为在可能包含types名称的域签名和方法签名中有特殊的含义。 在这些签名中, [开始数组types和; 标记引用types名称的结尾。

没有明确的理由. 是禁止的。 它不在JVM中使用,只在generics特征中有意义,但是如果使用generics特征,那么types名称会被进一步限制,因为不允许包含<> ,以及这些特征具有特殊含义在通用签名也。

因此,通过使用违反规范. 内部标识符不影响JVM的主要function。 有混淆器这样做。 生成的代码有效,但在请求通用types签名时可能会遇到reflection问题。 此外,通过replace所有/ s,将二进制名称转换为源名称. 如果二进制名称包含,则它将不可逆转. 秒。


可能有趣的是,有一个build议是在Java语法中支持所有可能的标识符 (参见第3点,“异类标识符”),但是它并没有进入到最终的Java 7中。似乎目前没有人作出了一个新的尝试来进入。


由于字节数存储为无符号短值,所以额外的技术限制是名称不能具有长于65535字节的修改的UTF-8表示 。

  1. 关键字只有编译器才知道。 编译器将它们转换成适当的字节码。 所以在编译的字节码的运行时间里它们不存在,因此没有被JVMvalidation。
  2. 当然,你不能访问在编译时不知道的类成员。 但是如果你确定这样的类成员将存在于编译的代码中(你将在那里“手工制作”),那么你可以使用reflection来实现这个目的,因为reflection访问没有被编译器validation。