關於 ++i 與 i++ 的經典誤區:「前置先加 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。
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 修改區域變量,**不影響操作數棧頂的舊值**,比較使用的是舊值。
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 (載入舊值)
iinc 指令執行時機相同)iinc 後 iload → 使用新值iload 後 iinc → 使用舊值(棧頂暫存副本)// ++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),語義更清晰理解字節碼層面的機制,才能在面試與實戰中不再被這類「基礎陷阱」困擾。