自 JDK 5.0 引入自動裝箱/拆箱機制後,Integer i = 127 這類寫法變得普遍。然而,許多開發者發現 == 比較在某些數值範圍內返回 true,超出範圍卻返回 false,而 equals() 始終正確。本文深入剖析其背後的整數快取機制。
public class IntegerCacheDemo {
public static void main(String[] args) {
// 測試案例 1:-128 到 127 範圍內
Integer x1 = 127;
Integer y1 = 127;
System.out.println("127 == 127 : " + (x1 == y1)); // true
System.out.println("127 equals : " + x1.equals(y1)); // true
// 測試案例 2:超出快取範圍
Integer x2 = 128;
Integer y2 = 128;
System.out.println("128 == 128 : " + (x2 == y2)); // false
System.out.println("128 equals : " + x2.equals(y1)); // true
// 直接 new 物件永不相等
Integer x3 = new Integer(127);
Integer y3 = new Integer(127);
System.out.println("new 127 == : " + (x3 == y3)); // false
}
}
查看 JDK 原始碼 java.lang.Integer,發現靜態內部類 IntegerCache:
private static class IntegerCache {
static final int low = -128;
static final int high;
static final Integer cache[];
static {
// 可透過 -XX:AutoBoxCacheMax= 調整上限,預設 127
int h = 127;
String integerCacheHighProp = sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
if (integerCacheHighProp != null) {
try {
int i = parseInt(integerCacheHighProp);
i = Math.max(i, 127);
h = Math.min(i, Integer.MAX_VALUE);
} catch (NumberFormatException nfe) {
// 忽略非法值
}
}
high = h;
cache = new Integer[(high - low) + 1];
int j = low;
for (int k = 0; k < cache.length; k++) {
cache[k] = new Integer(j++);
}
}
private IntegerCache() {}
}
自動裝箱時,Integer.valueOf(int) 會被呼叫:
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) 返回同一物件 → true超出範圍或 new Integer() → false | 嚴禁用於包裝類型數值比較 |
equals() | 比較數值內容。 無論快取與否,數值相等皆返回 true | 標準做法,但需注意空指針 |
Objects.equals() | 空值安全的內容比較 | 推薦最佳實踐 (JDK 7+) |
Byte:全範圍快取 (-128 ~ 127)Short:-128 ~ 127Long:-128 ~ 127Character:0 ~ 127 (Unicode BMP 範圍)Boolean:僅兩個單例 TRUE、FALSEFloat/Double:無快取 (浮點數精度問題)Objects.equals(a, b) 或 a.equals(b) 比較包裝類型數值 (前者空值安全)Integer.valueOf() 利用快取減少記憶體壓力-XX:AutoBoxCacheMax=2000 (僅影響上限,下限固定 -128)IntStream.range(-130, 130).forEach(i ->
System.out.printf("%4d : %b%n", i, Integer.valueOf(i) == Integer.valueOf(i))
);
輸出將清晰顯示 -128 ~ 127 為 true,其餘為 false。