經典面試題:int a=2; int b=a+3*a++; 結果 b=8 而非 9。許多開發者誤以為「優先級高的先算」,實則混淆了運算子優先級與運算元求值順序。
a+3*a++ 等價 a + (3 * (a++))。a++ 的值是舊值,副作用(a 變 3)在該運算元求值完成後立即生效,但不影響同一運算式中已取得的其他運算元副本。| 步驟 | 動作 | 堆疊/狀態 | a 的值 |
|---|---|---|---|
| 1 | 求值左運算元 a | 推入 2 | 2 |
| 2 | 遇到 +,需右運算元 | 暫存 | 2 |
| 3 | 求值 3 | 推入 3 | 2 |
| 4 | 遇到 *,優先級高,求值右運算元 a++ | 推入 2 (舊值) | 2 |
| 5 | 執行 ++ 副作用 | - | 3 (已修改) |
| 6 | 執行 *: 3 * 2 = 6 | 推入 6 | 3 |
| 7 | 執行 +: 2 + 6 = 8 | 結果 8 | 3 |
int a=2; int b=3*a++ + a; // b = 9
左至右求值:先算 3*a++ (得 6, a 變 3),再取右運算元 a (新值 3),結果 9。
JVM 使用運算元堆疊 執行位元組碼。編譯器將運算式轉為指令序列 (如 iload, iconst_3, iload, iinc, imul, iadd),iinc 直接修改區域變數表,堆疊保留的是 iload 時的舊值副本。這是實現細節,語言語意由 JLS 定義,不依賴堆疊機制。
a++ + a, a = a++)。int temp = a; a++; b = temp + 3 * temp; 语意清晰、無歧義。Sonar/Checkstyle 可設定規則檢測此類代碼異味。結論:Java 選擇「左至右求值 + 副作用不回溯」作為語言語意,編譯器實現方式 (堆疊/寄存器) 只是手段,不改變語意保證。