invokeinterface:调用一个接口方法在这段Java汇编代码中,addUser()方法是在第四行的“5:invokevitual#23″进行调用的。这表示对应索引为23的方法会被调用。索引为23的方法的名称已经被javap给注解在旁边了。invokevirtual是Java字节码里调用方法的最基本的操作码。在Java字节码里,有四种操作码可以用来调用一个方法,分别是:invokeinterface,invokespecial,invokestatic以及invokevirtual。操作码的作用分别如下:
invokespecial: 调用一个初始化方法,私有方法或者父类的方法
invokestatic:调用静态方法
invokevirtual:调用实例方法
Java字节码的指令集由操作码和操作数组成。类似invokevirtual这样的操作数需要2个字节的操作数。
用更新的类库来编译上面的应用代码,然后反编译它,将会得到下面的结果。
1
2
3
4
5
6
7
8
|
public void add(java.lang.String); Code: 0 : aload_0 1 : getfield # 15 ; //Field admin:Lcom/nhn/user/UserAdmin; 4 : aload_1 5 : invokevirtual # 23 ; //Method com/nhn/user/UserAdmin.addUser:(Ljava/lang/String;)Lcom/nhn/user/User; 8 : pop 9 : return |
你会发现,对应索引为23的方法被替换成了一个返回值为”Lcom/nhn/user/User”的方法。
在上面的反汇编代码里,代码前面的数字代码什么呢?
它表示的是字节数。大概这就是为什么运行在JVM上面的代码成为Java“字节”码的原因。简而言之,Java字节码指令的操作码,例如aload_0,getfield和invokevirtual等,都是用一个字节的数字来表示的(aload_0=0x2a,getfield=0xb4,invokevirtual=0xb6)。由此可知Java字节码指令的操作码最多有256个。
aload_0和aload_1这样的指令不需要任何操作数。因此,aload_0指令的下一个字节是下一个指令的操作码。不过,getfield和invokevirtual指令需要2字节的操作数。因此,getfiled的下一条指令是跳过两个字节,写在第四个字节的位置上的。十六进制编译器里查看字节码的结果如下所示。
1
|
2a b4 00 0f 2b b6 00 17 57 b1 |
表一:Java字节码中的类型表达式在Java字节码里,类的实例用字母“L;”表示,void 用字母“V”表示。通过这种方式,其他的类型也有对应的表达式。下面的表格对此作了总结。
Java Bytecode | Type | Description |
B | byte | signed byte |
C | char | Unicode character |
D | double | double-precision floating-point value |
F | float | single-precision floating-point value |
I | int | integer |
J | long | long integer |
L<classname> | reference | an instance of class <classname> |
S | short | signed short |
Z | boolean | true or false |
[ | reference | one array dimension |