标识符由 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”吗?
答案 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。
不以数字开头,可以更容易区分人类和解析器的标识符与整数文字。
另见: