Java中 i=i++ 問題底層原理解析

網際網路資深架構師發表於2020-12-13

有如下程式碼:

public class Main {
    public static void main(String[] args) {
        int i = 10;
        i = i++;
        System.out.println(i);
    }
}

執行後,發現結果為10,而不是11。這是為什麼呢?

首先先介紹一個JDK自帶的反編譯工具: javap命令。 可以看到到底底層是怎麼執行上面的程式碼的!

先編譯Main.java檔案

C:\leetcode\src\com\lilike>javac Main.java

此時在Main.java 目錄下生成了Main.class檔案。
然後執行javap命令:

C:\leetcode\src\com\lilike>javap -c Main
警告: 二進位制檔案Main包含com.lilike.Main
Compiled from "Main.java"
public class com.lilike.Main {
  public com.lilike.Main();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  public static void main(java.lang.String[]);
    Code:
       0: bipush        10
       2: istore_1
       3: iload_1
       4: iinc          1, 1
       7: istore_1
       8: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
      11: iload_1
      12: invokevirtual #3                  // Method java/io/PrintStream.println:(I)V
      15: return
}

上面的0,2,3,4,7操作,就是底層i = i ++的執行過程,我們先解釋這五項操作,在文章的末尾再補充虛擬機器指令的知識。

0: bipush        10  //將引數10壓入棧
2:istore_1     //棧中彈出一個數(10),賦值給區域性變數i(_1表示賦值給第一個區域性變數,即i)
3: iload_1  //將區域性變數i(_1表示第一個區域性變數,即i)的值入棧,此時棧頂的值為10
4: iinc     1, 1  //指令iinc對給定的區域性變數做自增操作
//1,1表示對第一個區域性變數i進行累加1操作,意味著i變為了11
7: istore_1   //棧頂彈出一個數:也就是10,賦值給第一個區域性變數i。意味著i的值又變回10了。(_1表示賦值給第一個區域性變數,即i)

最終列印結果 就是: 10

擴充知識-- java虛擬機器指令:
bipush:當int取值-128~127時,JVM採用bipush指令將常量壓入棧中。
istore:將棧頂int型數值存入指定本地變數
aload:從區域性變數表的相應位置裝載一個物件引用到運算元棧的棧頂。還有一些其他相似的操作碼用來裝載非物件引用,包括iload_、lload_、fload_和dload_,這裡的i代表int型,l代表long型,f代表float型以及d代表double型。
iinc:變數自增。

相關文章