以上两种方式,都挺方便的,根据自己喜好自由选择就好了。
相信你肯定也被很多字节码指令搞的一头雾水过,接下来,我们就一起学习下常用的指令都有哪些吧。之后,再来看上边的例子就轻松很多了。
数据类型我们知道 Java 是强类型语言,在使用之前肯定已经确定了它的类型。而数据类型,无非就是基本数据类型和引用类型。它们对应的字节码,其实就是用它们的对应类型的英文首字母来表示的。(引用类型除外)
例如,s 代表 short , i 代表 int, l 代表 long ,f 代表 float,d 代表 double,b 代表 byte(不包括boolean),c 代表 char,a 代表 reference 引用。
加载和存储指令我们知道,一个方法的运行,会在栈的栈帧中执行。方法中的变量称为局部变量,数的操作需要用到操作数栈。因此,加载和存储指令,就是数据在局部变量表和操作数栈中来回传输。
将一个局部变量加载到操作数栈:如 iload、iload_ 。表示加载的是 int 类型变量。
iload_ 后边带数字代表第几个 int 型变量。例如 iload_0 代表把第一个 int 型局部变量加载到操作数栈。
其他类型变量同上: lload ,fload ,dload,aload 。
将一个数值存储到局部变量表: istore,istore_,lstore,lstore_,fstore,fstore_,dstore,dstore_,astore,astore_ 。
将一个常量加载到操作数栈: aconst_null,iconst_m1,iconst_,lconst_,fconst_,dconst_ ,bipush,sipush。
ldc 代表把 int、float、String类型常量从常量池中加载到操作数栈。 ldc_w 代表宽索引。
ldc2_w 代表把 long 或 double 类型常量从常量池中加载到操作数栈。(宽索引)
这里需要说的是,int 类型根据数值的取值范围不同,而采用不同的字节码指令。
iconst_m1 代表 -1 ,iconst_ 代表 0~5 。bipush 代表 -128~127(byte取值范围), sipush 代表 -32768~32767(short取值范围),ldc 在 int 中代表 -2147483648~2147483647(int取值范围)。
public class TestByteCode { public static void main(String[] args) { int a = -1; int b = 0; int c = 1; int d = 2; int e = 3; int f = 4; int g = 5; int h = 127; int i = 32767; int j = 2147483647; } }其字节码为:
0 iconst_m1 1 istore_1 2 iconst_0 3 istore_2 4 iconst_1 5 istore_3 6 iconst_2 7 istore 4 9 iconst_3 10 istore 5 12 iconst_4 13 istore 6 15 iconst_5 16 istore 7 18 bipush 127 20 istore 8 22 sipush 32767 25 istore 9 27 ldc #2 <2147483647> 29 istore 10 31 return 访问指令访问类字段:getstatic,putstatic
访问类实例字段:getfield,putfield
方法调用和返回invokevirtual : 用于调用对象的实例方法。
invokeinterfce: 用于调用接口方法。
invokespecial:用于调用一些特殊的方法,如父类构造方法,实例初始化方法,私有方法。
invokestatic:用于调用类的静态方法。
invokedynamic:用于调用动态方法。
方法返回是跟返回类型相关,根据不同的返回类型,有不同的指令。
return:返回 void。
ireturn:注意,这个不止返回 int ,返回 boolean,byte,char,short 也用这个指令。其实,很多指令都没有直接支持 byte,char,short和 boolean,而是用 int 类型代替。这是因为,虚拟机的操作码长度只有一个字节,只能表示有限个数的指令。(我们这里所提到的所有指令,都只是方便我们记忆的助记符,而在计算机内部肯定还是一个字节,即 8 个 bit 位的二进制)
lreturn:返回 long 类型。
freturn:返回 float 类型。
dreturn:返回 double 类型。
areturn:返回引用类型。
其余字节码指令上边介绍的指令只是很少一部分字节码指令。但是,麻雀虽小,五脏俱全,也包括了最基本的变量定义,调用方法,和方法返回这些最基本的功能。同时,也足够我们去解释上边的问题,为什么注释行会被执行了。
更多的字节码指令可以参考《深入理解Java虚拟机》这本书的 6.4 节,和最后的附录字节码指令表。需要这本书的,可以微信搜索到我《烟雨星空》,后台回复“Java虚拟机”。
由于字节码指令太多,比如还有运算指令,包括加减乘除、位运算,比较指令等,if 等控制指令,类型强转指令,还有多线程用到的同步锁。And so on ~
不可能把它们全部记住背会,但是,其实都是由规律可循的,很多都是见名知意,用各种英文首字母简写代表。比如,int类型的加法运算,就是 iadd,double 类型的减法运算是 dsub。等等。
因此,我这里只是给了一个引子,重要的还是需要自己去寻找方法,不断的实践探索 ~
另外,官方文档才是最好的学习途径:https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-6.html
注释行字节码