《Java Puzzlers: Traps, Pitfalls, and Corner Cases》 (Joshua Bloch & Neal Gafter) 一書收錄 95 個反直覺程序,揭示 Java 語言暗礁。本文提煉核心陷阱分類與避坑原則。
// -3 % 2 = -1 (非 1)
// Java 取模結果符號與被除數 (左操作數) 一致
System.out.println(-3 % 2); // -1
System.out.println(3 % -2); // 1
原則:a % b 結果符號同 a。需正數餘數用 ((a % b) + b) % b。
System.out.println(2.00 - 1.10); // 0.8999999999999999
// 金融計算嚴禁 float/double
// 必須用 BigDecimal 或整數分單位
BigDecimal price = new BigDecimal("2.00");
BigDecimal pay = new BigDecimal("1.10");
System.out.println(price.subtract(pay)); // 0.90
long microsPerDay = 24 * 60 * 60 * 1000 * 1000; // 溢出!int 運算後再擴寬
// 24*60*60*1000*1000 = 86,400,000,000 > Integer.MAX_VALUE
// 正確:首個字面量加 L
long microsPerDay = 24L * 60 * 60 * 1000 * 1000; // 86400000000
原則:長整數運算首項加 L,強制全程 long 運算。
long l = 123L; **必須大寫 L**,小寫 l 極易與數字 1 混淆。char 擴展時**零擴展** (高位補 0),無符號。+= 等) 隱含強制轉型:byte b=1; b+=2; 等價 b=(byte)(b+2),但 b=b+2 編譯錯誤。char[] chars = {'a','b','c'};
String s = String.valueOf(chars); // 正確
// new String(chars) 也可,但 valueOf 處理 null 安全
System.out.println("Result: " + 1 + 2); // "Result: 12" (左結合)
// 正確
System.out.println("Result: " + (1 + 2)); // "Result: 3"
原則:+ 左結合,混合字符串與數字運算**必須加括號**。
// \u000A 是換行符,編譯器在解析前替換
// public static void main(String[] args) \u000A { // 編譯報錯:非法換行
// 最佳實踐:極少使用 Unicode 轉義,僅在字符字面量無法輸入時使用。
int i = 0;
i = i++ + i++; // i = 0 + 1 = 1 (副作用順序極難推理)
// 原則:單表達式中對同一變量賦值不超過一次。
public int finallyReturn() {
try { return 1; }
finally { return 2; } // 覆蓋 try 的返回值!
}
// 返回 2,try 的返回值被丟棄
// 原則:finally 块中**絕不**使用 return/throw/break/continue 離開。
try { throw new Exception("A"); }
finally { throw new Exception("B"); } // 異常 A 永遠丟失!
// 原則:finally 中任何可能拋出的異常必須內部捕獲處理,不得傳播。
異常僅用於「異常情況」,不可用於正常循環控制 (如用異常跳出循環)。性能差、可讀性差、破壞優化。
靜態字段在聲明類及其所有子類中**共享同一份拷貝**。子類無法「覆蓋」父類靜態字段,只能隱藏。
class Parent { static void foo() { System.out.println("Parent"); } }
class Child extends Parent { static void foo() { System.out.println("Child"); } }
Parent p = new Child();
p.foo(); // "Parent" (靜態綁定,按聲明類型調用)
Child c = new Child();
c.foo(); // "Child"
// 原則:靜態方法不參與多態,**永不**用實例調用靜態方法 (應用類名)。
null instanceof T 永遠返回 false (語言規定),無拋異常。
int i = 010; // 八進制 = 8 (十進制)
// 原則:整數字面量**絕不**以 0 開頭 (除非刻意寫八進制)。
// SimpleDateFormat 非線程安全
// 正確:ThreadLocal / DateTimeFormatter (Java 8+ 不可變、線程安全)
List list = List.of("a","b");
String[] arr = list.toArray(new String[0]); // 推薦:傳入長度 0 數組,JVM 優化分配精確大小
// 老寫法 new String[list.size()] 可能多分配/需拷貝