为什么for循环中的a=a++,a总是0
今天朋友去面试,遇到了一个非常"简单"的题目,如下
1
2
3
4
5
6
7
8
9
|
a = 0 ; for ( int i = 0 ; i < 10 ; i++) { a = a++; } System.out.println(a); //a = ? |
当时老张就一脸懵逼的说,a不是等于10吗??
朋友当时就说,培训出来的都知道a最后等于0
此时老张陷入了深深的思索之中,按理说是a取值a=0,a++=1,然后a=a++,十次之后当然等于10啊,
于是老张打开IDE写下如下代码并运行:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
class a{ public static void main(String [] args) { int a= 0 ; for ( int i = 0 ; i < 10 ; i++) { a=a++; } System.out.println(a); } } javac a.java java a. class |
结果竟然真的是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 }
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 }
与
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 }
对比如下指令
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的值
那么问题又回到了,i++和++i先加后加的路子上,这句话好耳熟
详细的说,现在我们一步步的分析下a=a++到底做了什么
1)inconst_0将0推送至栈顶
2)istore 将int型的数值存入第二个本地变量
3)iload_1 将第2个本地类型推送至栈顶
3)iinc 局部变量自增指令(注意是局部变量)
4)istore_1 将int型的值存入第二个局部变量表中(此时栈中的0将覆盖局部变量表中的1)
所以a=a++之所以结果为0,应为局部变量自增的值被覆盖了
所以a=++a的字节码指令聪明的你肯定也懂了
再简单的补充一下关于Java栈的一些知识
Java中栈是由栈帧组成,每个栈帧都是线程私有的.
每个栈帧包含4块内容:
1)局部变量表
局部变量表用来存储参数和局部变量,以slot为单位,除了long和double占2个slot,其余的6个基本类型和reference(引用)占1个slot
2)操作栈(操作数栈)
操作栈成为"基于栈的执行引擎",方法执行中进行算术运算或者是调用其他的方法进行参数传递的时候是通过操作数栈进行的