首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

Java 前置遞增(++i)與後置遞增(i++) 深度解析:字節碼視角還原真相

     編輯:Java 相關知識

關於 ++i 與 i++ 的經典誤區:「前置先加 1 再運算,後置先運算再加 1」。實際上,**兩者都先將變量加 1**,真正區別在於**運算使用的值**:前置使用加 1 後的值,後置使用加 1 前的舊值(通過暫存副本)。本文從源碼、字節碼、執行流程三個維度徹底釐清。

經典陷阱案例

案例 1:i = i++ 結果是多少?

int i = 100;
i = i++;
System.out.println(i); // 輸出 100

字節碼還原:

3: iload_1        // 載入 i (100) 到棧頂
4: iinc 1, 1      // 區域變量 1 自增 1 (i 變 101)
7: istore_1       // 將棧頂值 (100) 存回 i
8: getstatic ...
11: iload_1       // 載入 i (100) 打印

結論:i++ 將舊值 (100) 壓棧,iinc 修改區域變量為 101,istore_1 卻把棧頂的 100 覆寫回去,最終 i=100。

案例 2:while (k++ > 100) 循環輸出?

int k = 100;
while (true) {
    if (k++ > 100) break;
    System.out.println(k);
}
// 輸出:101(只打印一次)

字節碼流程:

3: iload_1        // 載入 k (100)
4: iinc 1, 1      // k 自增為 101
7: bipush 100     // 壓入 100
9: if_icmple 15   // 比較棧頂兩值:100 <= 100 成立 → 跳轉 15 (打印)
12: goto 25       // 101 > 100 → 跳出循環
15: iload_1       // 載入 k (101) 打印
...

關鍵點:iinc 修改區域變量,**不影響操作數棧頂的舊值**,比較使用的是舊值。

案例 3:方法返回值差異

static int pre() { int i = 55; return ++i; }  // 56
static int post() { int i = 55; return i++; } // 55

// 字節碼差異僅在 iinc 與 iload 順序對調:
// pre:  iinc → iload (載入新值)
// post: iload → iinc (載入舊值)

核心結論

  1. 統一自增時機:前置/後置 **都在運算前完成自增**(字節碼 iinc 指令執行時機相同)
  2. 運算值來源不同:
    • 前置:iinc 後 iload → 使用新值
    • 後置:iload 後 iinc → 使用舊值(棧頂暫存副本)
  3. 等價轉換:
    // ++i
    i += 1;
    j = i * 5;
    
    // i++
    int temp = i;
    i += 1;
    j = temp * 5;

實戰建議

  • 避免在複雜表達式中混用遞增遞減(如 a = i++ + ++i),可讀性極差且易出錯
  • 單獨語句中 i++ 與 ++i 效果等同,推薦獨立行使用
  • 循環變量更新推薦 for (int i = 0; i < n; ++i),語義更清晰

理解字節碼層面的機制,才能在面試與實戰中不再被這類「基礎陷阱」困擾。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved