为什么JVM允许我们命名以字节码中的数字开头的函数?

时间:2014-11-06 23:13:21

标签: java jvm bytecode identifier

标识符由 Java语言规范,Java SE 7版(第3.8节)定义

An identifier is an unlimited-length sequence of Java letters and Java digits, the
first of which must be a Java letter.

据我所知,由于方法名称是标识符,因此不可能在java中命名以数字开头的方法,javac尊重此规则。

那么,为什么Java虚拟机似乎不尊重这个规则,允许我们在Bytecode中命名一个以数字开头的函数?


这个简单的代码段实际上会打印f99()方法名称及其参数的值。

public class Test {
    public static void main(String[] args) {
        Test t = new Test();
        System.out.println(t.f99(100));
    }

    public int f99(int i){
        System.out.println(Thread.currentThread().getStackTrace()[1].getMethodName());
        return i;
    }
}

编译和执行:

$ javac Test.java
$ java Test

输出:

f99
100

可以在编译后反汇编代码,并按f99重命名所有99次出现(借助reJ等工具。

$ java Test

输出:

99
100

那么,该方法的名称实际上是“99”吗?

2 个答案:

答案 0 :(得分:7)

Java语言规范限制有效方法名称中的字符,以帮助明确解析Java语言。

JVM旨在支持Java以外的语言。因此,限制不应相同;除非我们想强制所有非Java语言具有相同的限制。为JVM选择的限制是允许对方法签名进行明确解析的最小集合,这种格式出现在JVM规范中而不是JLS中。

取自JVM Spec

a name must not contain any of the ASCII characters . ; [ / < > :

即,以下是有效的JVM签名[Lcom/foo/Bar;,其特殊字符已从方法名称中排除。

<>进一步保留用于将特殊JVM方法与应用程序方法分开,特别是<init><clinit>,它们都是JLS不允许的方法名称。

答案 1 :(得分:3)

那么,该方法的名称实际上是&#34; 99&#34;?

Real programmers不使用解析器,他们使用sed

javac Test.java
sed -i 's/\d003f99/\d00299/' Test.class
java Test

输出:

99
100

这是有效的,因为我们知道方法名称作为明文in a Utf8 entry存储在常量池中,而JVMS表示Utf8条目are of form

CONSTANT_Utf8_info {
    u1 tag;
    u2 length;
    u1 bytes[length];
}

所以我们有类似的东西:

01 | 00 03 | 'f' '9' '9'

(标识符长度为3个字节),sed命令用03 | 'f' '9' '9'替换02 | '9' '9'(现在长度为2个字节)。

我后来用javap -v Test.class检查sed做了我想做的事。之前:

#18 = Utf8               f99

后:

#18 = Utf8               99

手动编辑,运行,反编译并将.class与JVMS进行比较后,我只能断定方法名称必须为99: - )

所以它只是字节码中不存在的Java语言限制。

为什么Java会阻止此类名称?

可能使语法看起来像C。

不以数字开头,可以更容易区分人类和解析器的标识符与整数文字。

另见: