《Effective Java》第 2 章聚焦基本類型與包裝類交互易發的陷阱。本文結合源碼與實測,逐項拆解第 26-30 條建議的底層原理與最佳實踐。
List<Integer> list = Arrays.asList(1, 2, null);
int sum = list.stream().mapToInt(i -> i).sum(); // 隱式拆箱 → NPE
根因: 自動拆箱調用 Integer.intValue(),null 無法調用方法。
對策: 显式過濾 i != null ? i : 0,或用 Objects.requireNonNull() 攔截。結論:包裝類參與運算必做 null 校驗。
Integer a = new Integer(100), b = new Integer(100);
System.out.println(a == b); // false(比較引用)
System.out.println(a > b); // false(自動拆箱比較值,但 a==b 已誤導)
規則: == 比較引用,>/< 觸發拆箱比較值。**統一用 a.compareTo(b) 或 a.equals(b)**。
Integer.valueOf(int) 源碼揭示:
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high) // -128 ~ 127
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
實測:
Integer x = 127, y = 127; // 自動裝箱走 valueOf → 同一緩存對象 → x==y 為 true
Integer m = 128, n = 128; // 超出緩存 → 新建對象 → m==n 為 false
最佳實踐: 聲明包裝對象用 valueOf() 或直接賦值觸發自動裝箱;比較用 equals();可通過 -XX:AutoBoxCacheMax 擴大緩存上限。
void test(long a) { ... }
void test(Long a) { ... }
test(140); // 調用 long 版
test(new Integer(140)); // 自動拆箱 int → 加寬 long → 調用 long 版(非 Long 版!)
規則: 基本類型可「先加寬再裝箱」,不可「直接裝箱為寬類型包裝類」。混用重載極易調用非預期方法,**基本類型更安全、效能更高、可讀性更強**。
new Random() // 種子 = System.nanoTime() → 每次序列不同
new Random(1000) // 固定種子 → 同機器每次序列完全相同
原理: 偽隨機數由種子確定性生成。固定種子喪失隨機性,**除非為了測試復現,否則永不手動設種子**。
| 建議 | 核心口訣 | 關鍵 API |
|---|---|---|
| 26 | 拆箱前必判 null | i != null ? i : 0 |
| 27 | 包裝類比較用 compareTo/equals | Integer.compareTo() |
| 28 | 用 valueOf 享緩存 | Integer.valueOf() |
| 29 | 能用基本類型別用包裝類 | 基本類型優先 |
| 30 | 隨機種子交給系統 | new Random() |