问:如下java代码,最后a、b的值分别是多少?
public class Demo3_2 {
public static void main(String[] args) {
int a=10;
int b = a++ + ++a + a--;
System.out.println(a);//?
System.out.println(b);//?
}
}
一些有经验的开发人员,可能立马就能得出答案: a最后的值为11 ,b最后的值为34 , 那么为什么结果是这样呢?
本篇,笔者试图从jvm指令的层面来进行分析。
首先,我们用javap 命令反编译这段源码:
javap -v Demo3_2.class ,发现其中main方法生成的字节码指令如下:
public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=3, args_size=1
0: bipush 10
2: istore_1
3: iload_1
4: iinc 1, 1
7: iinc 1, 1
10: iload_1
11: iadd
12: iload_1
13: iinc 1, -1
16: iadd
17: istore_2
18: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
21: iload_1
22: invokevirtual #3 // Method java/io/PrintStream.println:(I)V
25: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
28: iload_2
29: invokevirtual #3 // Method java/io/PrintStream.println:(I)V
32: return
LineNumberTable:
line 32: 0
line 33: 3
line 34: 18
line 35: 25
line 36: 32
LocalVariableTable:
Start Length Slot Name Signature
0 33 0 args [Ljava/lang/String;
3 30 1 a I
18 15 2 b I
我们来分析下,这段字节码指令:
stack=2 表示栈桢中操作数栈的最大深度为2,locals=3表示局部变量表有3个槽位。槽位0是args ,也就是main方法的入参,槽位1和2分别对应局部变量a和b
bipush 10 执行引擎读取到该指令,将数字10压入操作数栈
istore_1 执行引擎将操作数栈中的值弹出,放入到局部变量表1号槽位,此时1号槽位的值为10
iload_1 执行引擎将局部变量表1号槽位的值压入操作数栈,此时操作数栈中存放的第一个值为10
iinc 1,1 执行引擎将局部变量表1号槽位变量值加1,此时1号槽位的值变为11
iinc 1,1 执行引擎将局部变量表1号槽位变量值加1,此时1号槽位的值变为12
iload_1 执行引擎将局部变量表1号槽位的值压入操作数栈,此时操作数栈中存放的第二个值为12
iadd 执行引擎执行将操作数栈的两个数字相加得到22【10+12】,此时操作数栈中只留下一个值22
iload_1 执行引擎将局部变量表1号槽位的值压入操作数栈,此时操作数栈中存放的第二个值为12
iinc 1,-1 执行引擎将局部变量表1号槽位变量值减1,此时1号槽位的值变为11
iadd 执行引擎将操作数栈的两个数字相加得到34【22+12】,此时操作数栈中只留下一个值34
istore_2 执行引擎将操作数栈中值弹出,放入局部变量表2号槽位,此时2号槽位的值为34
......
因此,最终的结果是:a的值为11,b的值为34
最后我们可以得出结论,i++和++i 的不同是在于字节码中:
i++ : 先执行iload (将局部变量表slot中的变量压入操作数栈) ,再执行iinc 1,1(局部变量表slot槽中变量自加1)
++i : 先执行iinc 1,1(局部变量表slot槽中变量自加1) ,再执行iload (将局部变量表slot中的变量压入操作数栈)