從字節碼來說明i++與++i到底有什麼不同

看字節碼之前需要先了解相關概念,如棧幀、操作數棧、局部變量表。
棧幀是JVM中很重要的一個概念,因爲JVM是基於棧的架構。一個方法的調用其實就是棧幀入棧出棧的過程。棧頂棧幀就是當前方法調用。
一個棧幀中包含:

  1. 局部變量表
  2. 操作數棧
  3. 動態鏈接
  4. 方法返回地址

這裏i++、 ++i涉及到的就是局部變量表和操作數棧。具體信息可參考:《Java虛擬機規範》

局部變量表存儲的是方法的參數以及內部定義的變量的值,操作數棧也是一個棧結構,用來執行方法中的指令。

好了,來看一個代碼片段:

class Scratch {

    public static void main(String[] args) {
        int i=0,j=0,m=0;
        j = i++;
        m = ++i;
    }
}

爲了不產生其他多過信息,這裏只寫了關鍵代碼。
首先可以通過javac 將源碼編譯成class:

javac scratch.java

執行完成後將看到Scratch.class 文件,通過javap命令查看字節碼:

javap -c Scratch > scratch.txt

爲了方便查看將結果輸出到了scratch.txt,打開此文件將看到如下信息:

Compiled from "scratch.java"
class Scratch {
  Scratch();
    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: iconst_0
       5: istore_3
       6: iload_1
       7: iinc          1, 1
      10: istore_2
      11: iinc          1, 1
      14: iload_1
      15: istore_3
      16: return
}

關注main方法 0-5,可以發現對應的是:

 int i=0,j=0,m=0;

主要是聲明變量並初始化爲0。我們可以發現下劃線後面跟了一個數字,這裏應該代表的是變量在局部變量表中的位置。
i:1
j:2
m:3

再關注main方法:6、7、10:
iload_1 表示將局部變量表中位置1的數據放入操作數棧中(這裏對應的是i,此時i的值爲0)然後pop出來賦值給j。然後再將i自增iinc。最後istore_2存儲j到局部變量表中。
操作完成後i=1,j=1。
最後關注main方法:11、14、15:
iinc首先自增i,然後iload_1將局部變量表中位置1的數據放入操作數棧中(這裏對應的是i,此時i的值爲2)然後pop出來賦值給m。最後istore_3存儲m到局部變量表中。
操作完成後i=2,j=1,m=2。

這就是爲什麼大家都說,i++是先賦值後自增,而++i是先自增後賦值的原因。

這裏要說明的是,如果是單獨的i++、++i是沒有什麼區別的。

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