題目一:Integer == 比較陷阱
Integer a = 1000, b = 1000;
Integer c = 100, d = 100;
System.out.println(a == b); // false
System.out.println(c == d); // true
核心機制:IntegerCache
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
// 預設快取範圍:-128 ~ 127
// 可透過 -XX:AutoBoxCacheMax=2000 擴大上限
解析
Integer c = 100 觸發自動裝箱 → Integer.valueOf(100) → 回傳快取物件 → c == d 為 true
Integer a = 1000 超出快取範圍 → 每次 new Integer(1000) → 不同物件參考 → false
題目二:字串駐留機制
String s1 = "abc";
String s2 = "abc";
String s3 = new String("abc");
System.out.println(s1 == s2); // true
System.out.println(s1 == s3); // false
記憶體模型
- 字串常量池:編譯期常量、
intern() 結果駐留於此 (JDK 7+ 在 Heap)
- 堆區:
new String() 強制建立新物件
s1、s2 指向同一常量池物件;s3 指向堆區新物件
補充:intern() 實戰
String s4 = new String("abc").intern(); // 強制放入/查找常量池
System.out.println(s1 == s4); // true
題目三:匿名內部類捕獲 final 變數
public void startTask(final String name) {
new Thread(() -> {
try { Thread.sleep(1000); } catch (InterruptedException ignored) {}
System.out.println(name); // 編譯器要求 name 為 final 或 effectively final
}).start();
}
為何需要 final?
- 生命週期不匹配:方法結束,區域變數
name 出棧銷毀;但執行緒可能稍後才執行
- 值複製機制:
final 讓編譯器在匿名類建構子隱式複製一份 name 副本 (val$name) 到實例欄位
- 語義保證:
effectively final (Java 8+) 表示變數初始化後未重新賦值,編譯器自動視為 final
反編譯視角
class Outer$1 implements Runnable {
final String val$name; // 複製的副本
Outer$1(String name) { this.val$name = name; }
public void run() { System.out.println(this.val$name); }
}
題目四:Integer 與 int 混合比較
Integer a = new Integer(1000);
int b = 1000;
Integer c = new Integer(10);
Integer d = new Integer(10);
System.out.println(a == b); // true
System.out.println(c == d); // false
關鍵規則:== 觸發自動拆箱
a == b:Integer 與 int 比較 → a 自動拆箱為 int → 數值比較 → true
c == d:兩個 Integer 物件參考比較 → new Integer(10) 建立不同物件 → false (雖 10 在快取範圍,但 new 繞過快取)
最佳實踐
- 優先使用基本型別
int,避免不必要的裝箱
- 比較包裝類型用
Objects.equals(a, b) 或 a.equals(b) (注意 NPE)
- 數值比較明確拆箱:
a.intValue() == b
- 避免在效能敏感路徑混用包裝/基本型別
總結:建立正確心智模型
| 場景 | 行為 | 建議 |
| Integer 裝箱 | -128~127 回傳快取物件 | 依賴快取行為屬實作細節,勿寫依賴此特性的業務邏輯 |
| 字串字面值 | 進入常量池,同值共享 | 比較內容用 equals(),== 僅用於檢查同一物件 |
| 匿名類捕獲區域變數 | 複製副本,要求 final/effectively final | Java 8+ 可省略 final 關鍵字,但語義不變 |
| 包裝類型 == 基本型別 | 觸發自動拆箱 | 明確拆箱或用 Objects.equals,避免隱式行為 |