常量池是 JVM 為提升效能、減少記憶體佔用而提供的「物件複用機制」。本文系統梳理 8 大基本型別包裝類快取範圍、String 常量池演進(JDK 7 移至堆)、intern() 手動入池與實戰避坑。
Integer i1 = 100; // 自動裝箱 → Integer.valueOf(100) Integer i2 = 100; System.out.println(i1 == i2); // true(快取命中) Integer i3 = 128; Integer i4 = 128; System.out.println(i3 == i4); // false(超出快取範圍,新建物件)
// Integer.valueOf 核心邏輯
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high) // low=-128, high=127
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
// IntegerCache 靜態初始化
private static class IntegerCache {
static final int low = -128;
static final int high = 127;
static final Integer cache[] = new Integer[high - low + 1];
static { for (int i = low; i <= high; i++) cache[i - low] = new Integer(i); }
}
| 包裝類 | 快取範圍 | 備註 |
|---|---|---|
| Boolean | TRUE/FALSE 兩個單例 | 全範圍快取 |
| Byte | -128 ~ 127 | 全範圍(byte 只有 256 個值) |
| Short | -128 ~ 127 | |
| Character | 0 ~ 127 (U+0000 ~ U+007F) | ASCII 範圍 |
| Integer | -128 ~ 127 | 可通過 -XX:AutoBoxCacheMax= 擴大上限 |
| Long | -128 ~ 127 | |
| Float | 無 | 不快取 |
| Double | 無 | 不快取 |
String s = "abc"; 編譯期確定,自動入池。String.intern():運行期手動將堆上 String 物件引用加入池中(若已存在則返回池中引用)。intern() 效能大幅提升。String s1 = new String("abc"); // 堆上新建 "abc" 物件,字面值 "abc" 入池
String s2 = "abc"; // 直接引用池中 "abc"
System.out.println(s1 == s2); // false(不同物件)
System.out.println(s1.intern() == s2); // true(intern 返回池中引用)
String s3 = new String("a") + new String("b"); // 堆上新建 "ab",未入池
String s4 = "ab"; // 字面值 "ab" 入池
System.out.println(s3.intern() == s4); // JDK 6: false;JDK 7+: true(intern 會將 "ab" 加入池,s4 已在池中)
== 比較字串內容,永遠用 equals()。intern(),會佔用堆記憶體且池無上限(JDK 7+),可能導致 OOM。-XX:StringTableSize= 可調整常量池哈希桶數量(預設 60013),提升 intern() 效能。equals() 比值,== 比引用(快取範圍內可能相等,不可依賴)。理解常量池機制,能寫出更省記憶體、更少 Bug 的 Java 代碼。