为什么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)操作栈(操作数栈)

        操作栈成为"基于栈的执行引擎",方法执行中进行算术运算或者是调用其他的方法进行参数传递的时候是通过操作数栈进行的

posted on 2019-07-20 16:54  mike_JP  阅读(1050)  评论(0编辑  收藏  举报

导航