從 JVM 層面理解 i++ 和 ++i 的真正區別!

語言: CN / TW / HK

前言

如果只用普通的知識解釋i++和++i的話

  • i++ 先將i賦值再++
  • ++i 先++再賦值

但是這簡單的回答並不能入吸引面試官的眼球,如果用java位元組碼指令分析則效果完全不同。

程式碼實現

public class OperandStackTest {
/**
    程式設計師面試過程中, 常見的i++和++i 的區別
     */
    public static void add(){
        //第1類問題:
        int i1 = 10;
        i1++;
        System.out.println(i1);//11

        int i2 = 10;
        ++i2;
        System.out.println(i2);//11

        //第2類問題:
        int i3 = 10;
        int i4 = i3++;
        System.out.println(i3);//11
        System.out.println(i4);//10

        int i5 = 10;
        int i6 = ++i5;
        System.out.println(i5);//11
        System.out.println(i6);//11

        //第3類問題:
        int i7 = 10;
        i7 = i7++;
        System.out.println(i7);//10

        int i8 = 10;
        i8 = ++i8;
        System.out.println(i8);//11

        //第4類問題:
        int i9 = 10;
        int i10 = i9++ + ++i9;//10+12
        System.out.println(i9);//12
        System.out.println(i10);//22
    }

    public static void main(String[] args) {
        add();
    }
}

執行結果

位元組碼指令

通過javap -v out目錄下的class檔名 在終端執行得到如下結果

 public static void add();
    descriptor: ()V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=2, locals=10, args_size=0
         0: bipush        10
         2: istore_0
         3: iinc          0, 1
         6: getstatic     #4                  // Field java/lang/System.out:Ljava/io/PrintStream;
         9: iload_0
        10: invokevirtual #5                  // Method java/io/PrintStream.println:(I)V
        13: bipush        10
        15: istore_1
        16: iinc          1, 1
        19: getstatic     #4                  // Field java/lang/System.out:Ljava/io/PrintStream;
        22: iload_1
        23: invokevirtual #5                  // Method java/io/PrintStream.println:(I)V
        26: bipush        10
        28: istore_2
        29: iload_2
        30: iinc          2, 1
        33: istore_3
        34: getstatic     #4                  // Field java/lang/System.out:Ljava/io/PrintStream;
        37: iload_2
        38: invokevirtual #5                  // Method java/io/PrintStream.println:(I)V
        41: getstatic     #4                  // Field java/lang/System.out:Ljava/io/PrintStream;
        44: iload_3
        45: invokevirtual #5                  // Method java/io/PrintStream.println:(I)V
        48: bipush        10
        50: istore        4
        52: iinc          4, 1
        55: iload         4
        57: istore        5
        59: getstatic     #4                  // Field java/lang/System.out:Ljava/io/PrintStream;
        62: iload         4
        64: invokevirtual #5                  // Method java/io/PrintStream.println:(I)V
        67: getstatic     #4                  // Field java/lang/System.out:Ljava/io/PrintStream;
        70: iload         5
        72: invokevirtual #5                  // Method java/io/PrintStream.println:(I)V
        75: bipush        10
        77: istore        6
        79: iload         6
        81: iinc          6, 1
        84: istore        6
        86: getstatic     #4                  // Field java/lang/System.out:Ljava/io/PrintStream;
        89: iload         6
        91: invokevirtual #5                  // Method java/io/PrintStream.println:(I)V
        94: bipush        10
        96: istore        7
        98: iinc          7, 1
       101: iload         7
       103: istore        7
       105: getstatic     #4                  // Field java/lang/System.out:Ljava/io/PrintStream;
       108: iload         7
       110: invokevirtual #5                  // Method java/io/PrintStream.println:(I)V
       113: bipush        10
       115: istore        8
       117: iload         8
       119: iinc          8, 1
       122: iinc          8, 1
       125: iload         8
       127: iadd
       128: istore        9
       130: getstatic     #4                  // Field java/lang/System.out:Ljava/io/PrintStream;
       133: iload         8
       135: invokevirtual #5                  // Method java/io/PrintStream.println:(I)V
       138: getstatic     #4                  // Field java/lang/System.out:Ljava/io/PrintStream;
       141: iload         9
       143: invokevirtual #5                  // Method java/io/PrintStream.println:(I)V
       146: return

解釋以上執行結果

第一類問題

對應的指令為

先將i1的值為10入棧(bipush),然後將int型別的值從棧中存到區域性變量表0的位置,然後執行iinc將0位置的值+1,然後將區域性變量表0位置的數入棧執行輸出操作

所以i1的值為11

先將i2的值為10入棧(bipush),然後將int型別的值從棧中存到區域性變量表1的位置,然後執行iinc將1位置的值+1,然後將區域性變量表1位置的數入棧執行輸出操作

所以i2的值為11

總結

由於沒有賦值操作,區別不大。

第二類問題

先將i3入棧儲存到區域性變量表2的位置,然後將它入棧,執行iinc將2位置的值加一,i4儲存到區域性表量表3的位置

所以i3是11,i4還是10

將i5入棧儲存到區域性變量表4的位置,由於是++i所以先iinc將4位置的值加一,然後將區域性變量表4的值入棧,執行賦值操作,所以都是11

第三類問題

先將i7入棧,然後存到區域性變量表6的位置,先把i6入棧,然後把6處的值加一,由於又將這個值儲存到區域性變量表6處,所以產生覆蓋又把值變為10。

而++i不會產生覆蓋先執行加一然後再把值入棧,在賦值給區域性變量表中,所以i8為11。

第四類問題

先將i9=10入棧,然後存在區域性變量表8的位置

int i10 = i9++ + ++i9;

先iload將8位置的i9入棧然後執行iinc將8處的i9加一,然後執行++i9,在將8處的i9加一

此時i9=10+1+1為12,然後將8位置的i9入棧,執行add將棧中的兩i9相加,得到的值儲存到區域性變量表9的位置

所以i10=10+12(i9++後還是10,++i9後是12,因為執行了兩次iinc操作)

然後呼叫虛方法和靜態方法,在將9處的值入棧執行輸出語句

原文連結:https://blog.csdn.net/demo_yo/article/details/118269423

版權宣告:本文為CSDN博主「默默無聞程式碼人」的原創文章,遵循CC 4.0 BY-SA版權協議,轉載請附上原文出處連結及本宣告。

近期熱文推薦:

1.1,000+ 道 Java面試題及答案整理(2021最新版)

2.別在再滿屏的 if/ else 了,試試策略模式,真香!!

3.臥槽!Java 中的 xx ≠ null 是什麼新語法?

4.Spring Boot 2.5 重磅釋出,黑暗模式太炸了!

5.《Java開發手冊(嵩山版)》最新發布,速速下載!

覺得不錯,別忘了隨手點贊+轉發哦!