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

《Java 解惑》讀書筆記精華:表達式、字符串、循環、異常、類與通用陷阱避坑指南

     編輯:Java 綜合教學

《Java 解惑》讀書筆記精華:表達式、字符串、循環、異常、類與通用陷阱避坑指南

《Java Puzzlers: Traps, Pitfalls, and Corner Cases》 (Joshua Bloch & Neal Gafter) 一書收錄 95 個反直覺程序,揭示 Java 語言暗礁。本文提煉核心陷阱分類與避坑原則。

一、表達式之謎

1. 奇數性陷阱

// -3 % 2 = -1 (非 1)
// Java 取模結果符號與被除數 (左操作數) 一致
System.out.println(-3 % 2); // -1
System.out.println(3 % -2); // 1

原則:a % b 結果符號同 a。需正數餘數用 ((a % b) + b) % b。

2. 找零謎題 (浮點數精度)

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

3. 長整除溢出

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 運算。

4. 初級問題 (字面量後綴)

  • long l = 123L; **必須大寫 L**,小寫 l 極易與數字 1 混淆。

5. 多重轉型規則

  • 有符號類型 (byte/short/int/long) 擴展時**符號擴展** (高位補符號位)。
  • char 擴展時**零擴展** (高位補 0),無符號。
  • 復合賦值 (+= 等) 隱含強制轉型:byte b=1; b+=2; 等價 b=(byte)(b+2),但 b=b+2 編譯錯誤。

二、字符串之謎

1. char[] 轉 String

char[] chars = {'a','b','c'};
String s = String.valueOf(chars); // 正確
// new String(chars) 也可,但 valueOf 處理 null 安全

2. 字符串連接優先級陷阱

System.out.println("Result: " + 1 + 2); // "Result: 12" (左結合)
// 正確
System.out.println("Result: " + (1 + 2)); // "Result: 3"

原則:+ 左結合,混合字符串與數字運算**必須加括號**。

3. Unicode 轉義字符陷阱

// \u000A 是換行符,編譯器在解析前替換
// public static void main(String[] args) \u000A {  // 編譯報錯:非法換行
// 最佳實踐:極少使用 Unicode 轉義,僅在字符字面量無法輸入時使用。

三、循環之謎

1. 單表達式多次賦值

int i = 0;
i = i++ + i++; // i = 0 + 1 = 1 (副作用順序極難推理)
// 原則:單表達式中對同一變量賦值不超過一次。

四、異常之謎

1. finally 中 return/break/continue

public int finallyReturn() {
    try { return 1; }
    finally { return 2; } // 覆蓋 try 的返回值!
}
// 返回 2,try 的返回值被丟棄
// 原則:finally 块中**絕不**使用 return/throw/break/continue 離開。

2. finally 中異常吞噬

try { throw new Exception("A"); }
finally { throw new Exception("B"); } // 異常 A 永遠丟失!
// 原則:finally 中任何可能拋出的異常必須內部捕獲處理,不得傳播。

3. 異常控制流程

異常僅用於「異常情況」,不可用於正常循環控制 (如用異常跳出循環)。性能差、可讀性差、破壞優化。

五、類之謎

1. 靜態字段共享性

靜態字段在聲明類及其所有子類中**共享同一份拷貝**。子類無法「覆蓋」父類靜態字段,只能隱藏。

2. 靜態方法隱藏非覆寫

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"
// 原則:靜態方法不參與多態,**永不**用實例調用靜態方法 (應用類名)。

3. instanceof 與 null

null instanceof T 永遠返回 false (語言規定),無拋異常。

六、庫之謎 / 通用陷阱

1. 八進制字面量陷阱

int i = 010; // 八進制 = 8 (十進制)
// 原則:整數字面量**絕不**以 0 開頭 (除非刻意寫八進制)。

2. 日期格式化線程不安全

// SimpleDateFormat 非線程安全
// 正確:ThreadLocal / DateTimeFormatter (Java 8+ 不可變、線程安全)

3. 集合 toArray 陷阱

List list = List.of("a","b");
String[] arr = list.toArray(new String[0]); // 推薦:傳入長度 0 數組,JVM 優化分配精確大小
// 老寫法 new String[list.size()] 可能多分配/需拷貝

總結:避坑金律

  1. 金融用 BigDecimal,浮點數只做科學計算。
  2. 長整數首項加 L,防止中間溢出。
  3. 字符串連接加括號,優先級陷阱殺人不見血。
  4. finally 不 return/throw,保護返回值與異常鏈。
  5. 靜態方法類名調用,別用實例,別指望多態。
  6. 整數字面量不以 0 開頭,拒絕八進制驚喜。
  7. 優先用庫 (Guava/Apache Commons),別重複造輪子踩坑。
  8. 寫單元測試覆蓋邊界,讓編譯器和測試幫你發現《解惑》級陷阱。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved