Java中前置++与后置++的深入学习

前些天看到一篇关于“毒代码”的文章(https://www.jianshu.com/p/3d7dfbfec566),文章的内容还不错,不过有一个问题始终觉得解释的不够透彻,问题如下

关于result = num++和result = ++num的问题,我想早在大学时,老师已经都讲臭了,前置++先自加后赋值,后置++先赋值后自增。

所以当笔者第一眼看到上面那个问题的时候,第一反应是输出99,因为很自然的会觉得 num = num++ 会被分解为:

1:num = num;

2:num++;两步

也就是说将0赋值给num,然后num自增为1,如此循环,但是事实却不是这样,上面的结果为0!

我们注意这里两个表达式的区别 result = num++ 和 num = num++ 这两种写法可是完全不一样的,至于怎么不一样继续往下看。

看了文章中的解释,感觉也是云里雾里,为什么num++没起作用?为什么++后num值没变?难道上学的时候老师说的是错的?

带着这些问题,我们来看下生成的字节码,分析一下,前置++和后置++过程中,JVM究竟做了什么。

首先java代码

public class TestMainClass1 {
    public static void main(String[] args) {
        int num=0;
        num = num++;
        System.out.println(num);
    }
}

public class TestMainClass1 {
    public static void main(String[] args) {
        int num=0;
        num = ++num;
        System.out.println(num);
    }
}

javac生成字节码后,通过javap -c 查看

public class com.aurora.test.TestMainClass1 {
  public static void main(java.lang.String[]);
    Code:
       0: iconst_0    // 整数0入栈
       1: istore_1    // 将0弹出栈,并存储到局部变量1
       2: iload_1    // 将局部变量1的值0入栈
       3: iinc          1, 1    // 局部变量1增加1,值变为1,不操作栈
       6: istore_1    // 0出栈,并存储到局部变量1
       7: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
      10: iload_1
      11: invokevirtual #3                  // Method java/io/PrintStream.println:(I)V
      14: return
}


public class com.aurora.test.TestMainClass1 {
  public static void main(java.lang.String[]);
    Code:
       0: iconst_0    // 整数0入栈
       1: istore_1    // 将0弹出栈,并存储到局部变量1
       2: iinc          1, 1    // 局部变量1增加1,值变为1,不操作栈
       5: iload_1    // 将局部变量1的值1入栈
       6: istore_1    // 1出栈,值存储到局部变量1
       7: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
      10: iload_1
      11: invokevirtual #3                  // Method java/io/PrintStream.println:(I)V
      14: return
}

可以看到区别就是iload和iinc这两个操作顺序的问题,

后置++在iinc增加局部变量的值后,又从操作栈中将变量值istore回了局部变量中,等于iinc白加了。

而前置++是先修改局部变量值,然后局部变量入栈,最后出栈存入局部变量,这一套下来,局部变量的值是变了的。

单纯的文字可能还不太好理解,这里让我这个灵魂画手用图表示下(非战斗人员请迅速撤离):

前置++我就不画了(我画的闹眼睛),本质就是先iinc自增,然后将自增后的值入栈,然后再出栈存入局部变量表。

那么再回到  result = num++ 和 num = num++ 的问题,第一种是赋值给了新变量,第二种是赋值给了num,也就是图中画的那种操作。赋给新变量后,num不会被覆盖,而赋值给num自身的话,因为num自增后结果被出栈的值0给覆盖了,所以num最后还是0.

下面还有几篇文章,也从不同角度讲了这个问题,可以参考看下

1、https://blog.csdn.net/qiaoquan3/article/details/52832640?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-6.control&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-6.control

2、https://www.cnblogs.com/thiaoqueen/p/8466359.html

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章