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

Can Java class files use reserved keywords as names?

我知道 Java-the-compilable-programming-language 与 Java-the-bytecode-format-for-JVM-execution 不同。有一些在 .class 格式中有效但在 .java 源代码中无效的示例,例如无构造函数 classes 和合成方法。

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

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

  1. 只有编译器知道关键字。编译器翻译 它们变成足够的字节码。所以它们在运行时不存在 已编译的字节码,因此未被 JVM 验证。
  2. 当然,您无法访问不为人所知的 class 成员 编译时间。但是如果你是,你可以为此目的使用反射 确保这样的 class 成员将存在于编译代码中(你 "hand-craft" 他们会在那里),因为反射访问不是 由编译器验证。

是的,您可以使用保留字。这些话仅适用于编译器。它们不会出现在生成的字节码中。

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

这是合法的 Scala:

class `class`

这定义了一个名为 class 的 class 和一个无参数的构造函数。 运行 javap(反汇编程序)在已编译的 class.class 文件中显示

public class class {
    public class();
}

Scala 可以对任何其他 Java 保留字执行相同的操作。

class int
class `while`
class goto

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

正如您所怀疑的,您将无法使用 Java 中的这些 classes,除了反射。您 可以 从类似的 "customized" class 文件中使用这些文件,例如来自 Scala 编译器生成的 class 文件。

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

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

理论上您甚至可以在 class 名称中使用空字符,但由于文件名中不可能有空字符,因此您不能在 jar 中包含这样的 class 文件.不过,您也许可以动态创建和加载一个。

这是您可以执行的一些操作的示例(用 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

有关 JVM 规范中规则的准确引用,请参阅

关于名称的限制已在 JVM 规范中修复:

§4.2.1. Binary Class and Interface Names

Class and interface names that appear in class file structures are always represented in a fully qualified form known as binary names (JLS §13.1). Such names are always represented as CONSTANT_Utf8_info structures (§4.4.7) and thus may be drawn, where not further constrained, from the entire Unicode codespace…

For historical reasons, the syntax of binary names that appear in class file structures differs from the syntax of binary names documented in JLS §13.1. In this internal form, the ASCII periods (.) that normally separate the identifiers which make up the binary name are replaced by ASCII forward slashes (/). The identifiers themselves must be unqualified names (§4.2.2).  

§4.2.2. Unqualified Names

Names of methods, fields, local variables, and formal parameters are stored as unqualified names. An unqualified name must contain at least one Unicode code point and must not contain any of the ASCII characters . ; [ / (that is, period or semicolon or left square bracket or forward slash).

Method names are further constrained so that, with the exception of the special method names <init> and <clinit> (§2.9), they must not contain the ASCII characters < or > (that is, left angle bracket or right angle bracket).

所以答案是,只有少数字符不能在二进制级别上使用。首先,/ 是包分隔符。那么,;[ 就不能用了,因为它们在 field signatures and method signatures 中有特殊含义,可能包含类型名称。在这些签名中,[ 开始一个数组类型,; 标记引用类型名称的结尾。

. 被禁止的原因不明。它不在 JVM 中使用,仅在 generic signatures 中有意义,但 如果 您使用的是通用签名,则类型名称进一步受到限制,不允许包含 <>: 以及这些字符在通用签名中也有特殊含义。

因此,在标识符中使用 . 违反规范不会影响 JVM 的主要功能。有混淆器这样做。生成的代码可以工作,但在请求通用类型签名时您可能会遇到反射问题。此外,如果二进制名称包含 .s.

,则通过将所有 /s 替换为 .s 将二进制名称转换为源名称将变得不可逆

有一个 proposal to support all possible identifiers within Java syntax 可能很有趣(见第 3 点,“异国情调的标识符”),但它没有进入最终的 Java 7。看起来,目前还没有人尝试将其引入。


存在额外的技术限制,即名称不能 Modified UTF-8 representation 超过 65535 字节,因为字节数存储为无符号短值。