很早之前遇到了一个非常有意思的题目,突然想起来,如下
a = 0; for(int i = 0; i < 10; i++) { a = a++; } System.out.println(a); //a = ?
按理说,a最后等于10,但是IDE的执行结果是0.
使用javap命令查看字节码命令
javap -c a.class
得到如下代码
Compiled from "a.java" class a { a(); Code: 0: aload_0 1: invokespecial #1 // Method java/lang/Object."<init>":()V 4: return public static void main(java.lang.String[]); Code: 0: iconst_0 1: istore_1 2: iconst_0 3: istore_2 4: iload_2 5: bipush 10 7: if_icmpge 21 10: iload_1 11: iinc 1, 1 14: istore_1 15: iinc 2, 1 18: goto 4 21: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream; 24: iload_1 25: invokevirtual #3 // Method java/io/PrintStream.println:(I)V 28: return }
一目了然,不如我们这样看更简单
class a{ public void test() { int a=0; a=a++;//a=0 } }
使用javap -c
Compiled from "a.java" class a { a(); Code: 0: aload_0 1: invokespecial #1 // Method java/lang/Object."<init>":()V 4: return public void test(); Code: 0: iconst_0 //a=0 //将int型的0推送至栈顶 1: istore_1 // //将int型的数值存入第二个本地变量 2: iload_1 // //将第二个int型本地变量推送至栈顶 3: iinc 1, 1 // //++操作 局部变量自增指令 6: istore_1 // //将int型的数值存入第二个本地变量 7: return }
与
<wiz_code_mirror>
class a{ public void test() { int a=0; a=++a; //a=1 } }
Compiled from "a.java" class a { a(); Code: 0: aload_0 1: invokespecial #1 // Method java/lang/Object."<init>":()V 4: return public void test(); Code: 0: iconst_0 1: istore_1 2: iinc 1, 1 5: iload_1 6: istore_1 7: return }
对比code
a++
0: iconst_0 //a=0 //将int型的0推送至栈顶
1: istore_1 // //将int型的数值存入第二个本地变量
2: iload_1 // //将第二个int型本地变量推送至栈顶
3: iinc 1, 1 // //++操作 局部变量自增指令
6: istore_1 // //将int型的数值存入第二个本地变量
7: return
++a
0: iconst_0 //a=0 //将int型的0推送至栈顶
1: istore_1 // //将int型的数值存入第二个本地变量
2: iinc 1, 1 // //++操作 局部变量自增指令
5: iload_1 // //将第二个int型本地变量推送至栈顶
6: istore_1 // //将int型的数值存入第二个本地变量
7: return
所有至关重要的异步就是: iinc(局部变量自增)指令与iload_1指令的先后顺序决定的a的值
1)inconst_0将0推送至栈顶
2)istore 将int型的数值存入第二个本地变量
3)iload_1 将第2个本地类型推送至栈顶
3)iinc 局部变量自增指令(注意是局部变量)
4)istore_1 将int型的值存入第二个局部变量表中(此时栈中的0将覆盖局部变量表中的1)
所以a=a++之所以结果为0,应为局部变量自增的值被覆盖了
再简单的补充一下关于Java栈的一些知识
Java中栈是由栈帧组成,每个栈帧都是线程私有的.
每个栈帧包含4块内容
1)局部变量表
局部变量表用来存储参数和局部变量,以slot为单位,除了long和double占2个slot,其余的6个基本类型和reference类型占1个slot
2)操作栈(操作数栈)
操作栈成为"基于栈的执行引擎",方法执行中进行算术运算或者是调用其他的方法进行参数传递的时候是通过操作数栈进行的
3)动态链接
略
4)返回地址
略