不可變類實例狀態不變,天然線程安全,適合共享。重用高頻實例可節省內存、減少GC壓力、提升性能。JDK 1.5在Integer、Short、Byte、Character、Boolean等包裝類引入靜態工廠方法valueOf()實現實例緩存。
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調整上限)。緩存實際為Integer[]數組,類加載時預創建。
Integer a = new Integer(10);
Integer b = new Integer(10);
Integer c = Integer.valueOf(10);
Integer d = Integer.valueOf(10);
System.out.println(a == b); // false:new每次創建新對象
System.out.println(a == c); // false:new vs 緩存
System.out.println(c == d); // true:同一緩存實例
僅創建3個對象:兩個new實例 + 一個緩存實例。最佳實踐:優先使用valueOf()獲取包裝類實例,除非明確需要獨立對象(極少見)。
緩存設計需考慮:何時入緩存、何時清理失效引用、併發安全。簡單實現採用集合 + 軟引用(SoftReference):
public final class Name {
private final String firstName;
private final String lastName;
// 緩存:Set>,JVM內存不足時自動回收
private static final Set> cache =
Collections.synchronizedSet(new HashSet<>());
private Name(String first, String last) {
this.firstName = first;
this.lastName = last;
}
public static Name valueOf(String first, String last) {
// 1. 遍歷緩存查找匹配實例
synchronized (cache) {
for (SoftReference ref : cache) {
Name n = ref.get();
if (n != null && n.firstName.equals(first) && n.lastName.equals(last)) {
return n; // 命中緩存
}
}
}
// 2. 未命中:創建新實例,軟引用入緩存
Name newInstance = new Name(first, last);
cache.add(new SoftReference<>(newInstance));
return newInstance;
}
// equals/hashCode基於firstName+lastName實現,省略...
}
關鍵點:
final、字段final、無setter,保證不可變性。Set去重,SoftReference避免內存洩漏(JVM內存緊張時自動清理)。valueOf同步遍歷+創建,簡單但高併發下有性能瓶頸。生產級可用ConcurrentHashMap + WeakReference + ReferenceQueue實現高效緩存(如Guava Interner)。new構造器供一次性實例使用,避免污染緩存。| 類 | 緩存範圍/策略 | 備註 |
|---|---|---|
| Boolean | TRUE/FALSE兩個單例 | valueOf(boolean)直接返回常量 |
| Byte | -128 ~ 127 全範圍 | byte只有256值,全緩存 |
| Short | -128 ~ 127 | 同Integer默認範圍 |
| Character | \u0000 ~ \u007F (0-127) | 基本ASCII範圍 |
| Long | -128 ~ 127 | JDK 8+引入 |
| String | 字符串常量池 | 編譯期常量、intern()手動入池 |