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

 

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