目标,学会阅读字节码!
事实上整个执行过程都是操作数栈和局部变量表的交互过程。
加载和存储指令
加载到操作数栈中,为了效率,应对不同长度的需要,设计了不同长度的指令。
类型 | 常数指令 | 范围 |
---|---|---|
int(boolean,byte,char,short) | iconst | [-1, 5] |
bipush | [-128, 127] | |
sipush | [-32768, 32767] | |
ldc | any int value | |
long | lconst | 0, 1 |
ldc | any long value | |
float | fconst | 0, 1, 2 |
ldc | any float value | |
double | dconst | 0, 1 |
ldc | any double value | |
reference | aconst | null |
ldc | String literal, Class literal |
存储,保存操作数栈栈顶元素到局部变量表中。
xstore(x为i、l、f、d、a)、xstore_n(x为i、l、f、d、a,n为0至3)
。
案例:
// 为什么输出10?
public void method8(){
int i = 10;
i = i++;
System.out.println(i);//10
}
// 10压入操作数栈
0 bipush 10
// 操作数栈顶出栈,保存到局部变量表1位置,0是this
2 istore_1
// 加载局部变量表1(即10)到操作数栈
3 iload_1
// 局部变量表1位置增加1(变为11)
4 iinc 1 by 1
// 操作数栈顶出栈(10),保存到局部变量表1,覆盖11
7 istore_1
// 获得静态对象,out,保存安东操作数栈
8 getstatic #2 <java/lang/System.out : Ljava/io/PrintStream;>
// 加载局部变量表1(10)
11 iload_1
// 调用方法,打印
12 invokevirtual #3 <java/io/PrintStream.println : (I)V>
15 return
类型转换指令
类型转换包括宽化类型转换(小到大):int–>long–>float-> double;
窄化类型转换(大到小,截断)。
窄化类型转换可能发生精度丢失,宽化类型转换也有可能
- 宽化类型转换是不会因为超过目标类型最大值而丢失信息的,例如,从int转换到long,或者从int转换到double,都不会丢失任何信息,转换前后的值是精确相等的。
- 从int、long类型数值转换到float,或者long类型数值转换到double时,将可能发生精度丢失一一可能丢失掉几个最低有效位上的值,转换后的浮点数值是根据IEEE754最接近含入模式所得到的正确整数值。
byte | char | short | int | long | float | double |
---|---|---|---|---|---|---|
int | i2b | i2c | i2s | ○ | i2l | i2f |
long | l2i i2b | l2i i2c | l2i i2s | l2i | ○ | l2f |
float | f2i i2b | f2i i2c | f2i i2s | f2i | f2l | ○ |
double | d2i i2b | d2i i2c | d2i i2s | d2i | d2l | d2f |
创建指令与字段访问指令
创建指令 | 含义 |
---|---|
new | 创建类实例 |
newarray | 创建基本类型数组 |
anewarray | 创建引用类型数组 |
multilanewarra | 创建多维数组 |
创建指令的特点是,在堆中创建出对象后会把地址压入栈帧。
字段访问指令 | 含义 |
---|---|
getstatic、putstatic | 访问类字段(static字段,或者称为类变量)的指令 |
getfield、 putfield | 访问类实例字段(非static字段,或者称为实例变量)的指令 |
对象创建后,就可以通过对象访问指令获取对象实例或数组实例中的字段或者数组元素。
public void setOrderId(){
// 新建对象
Order order = new Order