每個 Java 開發者的成長之路,都伴隨著無數次「為什麼不工作?」的深夜除錯。本文系統整理了從**環境配置、語法誤解、面向對象誤區、集合陷阱、併發隱患**五大維度的十大高頻錯誤,提供**錯誤代碼、報錯特徵、根因分析、最佳實踐**四步法解析,助您建立「防禦性編程」肌肉記憶,少走彎路。
// ❌ 現象:javac 編譯通過,java 運行報錯 // 原因 1: 類名與檔案名不符 (Public 類必須與 .java 同名) // 原因 2: package 聲明與目錄結構不匹配 // 原因 3: CLASSPATH 環境變量汙染或未包含當前目錄 (.) // 原因 4: 未進入正確目錄執行 java 命令 // ✅ 最佳實踐 // 1. 严格遵循: Public 類名 == 檔案名 (區分大小寫) // 2. 目錄結構 = 包結構: com/example/App.java -> package com.example; // 3. **拒絕設定全局 CLASSPATH 環境變量** (汙染極大) // 4. 使用構建工具 統一管理依賴與編譯運行 // 5. IDE 開發時信任 IDE 內建構建系統,命令行僅用於構建產物驗證
// ❌ 經典災難
int day = 2;
switch (day) {
case 1: System.out.println("Mon");
case 2: System.out.println("Tue"); // 會打印 Tue, Wed, Thu...
case 3: System.out.println("Wed");
default: System.out.println("Other");
}
// ✅ Java 12+ Switch Expression (無穿透、返回值、箭頭語法)
String dayName = switch (day) {
case 1 -> "Mon";
case 2 -> "Tue";
case 3 -> "Wed";
default -> "Other";
};
// ✅ 必須用 break (舊版) 或 yield (塊語法)
switch (day) {
case 1: System.out.println("Mon"); break;
case 2: System.out.println("Tue"); break;
// ...
}
// ❌ 比較引用地址,非內容
String s1 = new String("hello");
String s2 = new String("hello");
System.out.println(s1 == s2); // false (不同對象)
System.out.println(s1.equals(s2)); // true
// ⚠️ 整數緩存陷阱 (-128 到 127)
Integer a = 127, b = 127; System.out.println(a == b); // true (緩存)
Integer c = 128, d = 128; System.out.println(c == d); // false (新對象!)
// ✅ 鐵律:比較內容永遠用 .equals()
// ✅ 基本類型才用 ==
// ✅ 枚舉比較可用 == (單例保證) 或 .equals()
// ❌ 0.1 + 0.2 != 0.3 System.out.println(0.1 + 0.2); // 0.30000000000000004 if (0.1 + 0.2 == 0.3) ... // 永遠 false // ✅ 金融/精確計算:BigDecimal BigDecimal bd1 = BigDecimal.valueOf(0.1); BigDecimal bd2 = BigDecimal.valueOf(0.2); System.out.println(bd1.add(bd2)); // 0.3 // ✅ 科學計算/容差比較 double eps = 1e-9; if (Math.abs((0.1+0.2) - 0.3) < eps) ... // 相對/絕對誤差判斷
// ❌ 貧血模型 + 破壞不變量
public class User {
public String name; // 直接暴露
public int age; // 可設負數
}
// ✅ 封裝 + 不變量保護
public class User {
private final String name; // 不可變
private int age; // 可控變更
public User(String name, int age) {
if (name == null || name.isBlank()) throw new IllegalArgumentException("姓名不可空");
if (age < 0 || age > 150) throw new IllegalArgumentException("年齡非法");
this.name = name;
this.age = age;
}
// Getter/Setter 帶驗證
public void setAge(int age) { if (age>=0) this.age = age; }
}
// Java 唯有「值傳遞」:基本類型傳值,引用類型傳「引用的值」(地址副本)
public void swap(String a, String b) { // 無法交換外部引用指向
String t = a; a = b; b = t; // 僅交換局部變量
}
public void modifyList(List list) {
list.add("new"); // ✅ 修改對象內容:成功 (同一對象)
list = new ArrayList<>(); // ❌ 修改引用指向:外部無感 (副本指向新對象)
}
// ✅ 需交換/返回新對象時,返回值或包裝類/陣列/集合
public List addAndReturnNew(List list) {
List copy = new ArrayList<>(list);
copy.add("new");
return copy;
}
// ❌ 陷阱 1: 捕獲局部變量必須 final 或 effectively final int count = 0; list.stream().forEach(s -> count++); // 編譯錯誤: count not final // ✅ 用 AtomicInteger / 長陣列 / 收集器 AtomicInteger cnt = new AtomicInteger(); list.stream().forEach(s -> cnt.incrementAndGet()); // ✅ 或直接用收集器 long count = list.stream().count(); // ❌ 陷阱 2: 並行流無序 list.parallelStream().forEach(System.out::print); // 順序亂 // ✅ 有序需求用 forEachOrdered / 串行流 // ❌ 陷阱 3: 副作用 (修改外部狀態) 違背函數式原則 Listresult = new ArrayList<>(); list.stream().filter(s -> s.length()>3).forEach(result::add); // 串行勉強可,並行錯 // ✅ 純函數式收集 List result = list.stream().filter(s -> s.length()>3).collect(Collectors.toList());
| 陷阱 | 現象 | 對策 |
|---|---|---|
SimpleDateFormat 非線程安全 |
多線程格式化日期亂碼/拋異常 | 用 ThreadLocal / DateTimeFormatter (JDK 8+, 線程安全) |
equals/hashCode 契約違反 |
放入 HashSet/HashMap 找不到/重複 |
**必須同時重寫**,IDE 自動生成,包含所有業務欄位 |
| 資源未關閉 (檔案/連接/流) | 句柄洩漏、連接池耗盡 | Try-with-resources (JDK 7+) 強制自動關閉 |
空指針 NullPointerException |
隨機崩潰 | Objects.requireNonNull、Optional、註解 @NonNull、防御性檢查 |
BigDecimal 建構子精度陷阱 |
`new BigDecimal(0.1)` -> 0.10000000000000000555... | **永遠用 `BigDecimal.valueOf(double)` 或 `new BigDecimal(String)`** |
序列化 serialVersionUID 缺失 |
類修改後反序列化失敗 InvalidClassException |
**顯式聲明 `private static final long serialVersionUID = 1L;`** |
異常吞噬 catch (Exception e) {} |
Bug 隱形、難以排查 | **至少記錄日誌 `log.error("", e)`**,或拋出自定義異常 |
錯誤是學費,但**同樣的錯誤犯兩次就是能力不足**。將這份指南收藏,每次遇到詭異 Bug 時對照排查,您的 Java 功力將在「填坑」中穩步昇華。