前言
在Java中,每一个基本类型都有对应的包装类。其中,Integer
作为最常用的包装类之一,其内部实现巧妙地运用了享元模式(Flyweight Pattern),通过对象缓存机制显著提升了性能。本文将深入剖析Integer
类的享元模式实现,重点解析valueOf()
方法的底层源码。
一、享元模式是什么?
享元模式是一种结构型设计模式,旨在通过共享技术有效地支持大量细粒度对象的复用。在Java包装类中,享元模式的核心思想是:
- 缓存常用对象:对一定范围内的值进行预缓存
- 减少对象创建:通过复用缓存对象降低内存开销
- 提升性能:避免频繁的对象创建与垃圾回收
在Java中,包装类如Integer
便采用了享元模式,以缓存常用的整数值,提升性能。
二、Integer源码解析
valueOf()方法
Integer
类的valueOf()
方法是享元模式的核心,它通过缓存机制避免了频繁的对象创建。具体实现如下:
valueOf()
方法源码:
@IntrinsicCandidate
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
当调用valueOf()
方法时,首先会检查传入的整数是否在缓存的范围内:
- 在缓存范围内:直接返回缓存中的对象,避免重新创建。
- 不在缓存范围内:创建新的
Integer
对象。
IntegerCache类
IntegerCache
类实现了缓存机制,缓存了从 -128 到 127 的所有Integer
对象。其源码如下:
/**
* Integer 缓存类(享元模式实现)
* 用于缓存常用 Integer 对象,优化内存和性能
*/
private static class IntegerCache {
static final int low = -128; // 缓存下限
static final int high; // 缓存上限(可配置)
static final Integer[] cache; // 缓存数组
static Integer[] archivedCache; // 从 CDS 归档文件加载的缓存数组(JDK 17 新增)
static {
int h = 127; // 默认缓存上限为 127
// 尝试读取 JVM 参数配置的缓存上限
String integerCacheHighPropValue =
VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
if (integerCacheHighPropValue != null) {
try {
// 确保配置值不低于 127
h = Math.max(parseInt(integerCacheHighPropValue), 127);
// 防止缓存数组大小超过 Integer.MAX_VALUE
// 计算逻辑:数组最大长度 = Integer.MAX_VALUE - (-low) -1
h = Math.min(h, Integer.MAX_VALUE - (-low) -1);
} catch( NumberFormatException nfe) {
// 忽略格式错误(保持默认值 127)
}
}
high = h; // 确定最终缓存上限
// 尝试从 CDS 归档文件加载缓存(JDK 17 优化点)
CDS.initializeFromArchive(IntegerCache.class);
int size = (high - low) + 1;
// 动态生成缓存数组的条件:
// 1. 归档缓存不存在 或 2. 当前需要的缓存大小 > 归档缓存长度
if (archivedCache == null || size > archivedCache.length) {
// 创建新缓存数组
Integer[] c = new Integer[size];
int j = low; // 起始值
for(int i = 0; i < c.length; i++) {
c[i] = new Integer(j++); // 预生成所有缓存对象
}
archivedCache = c; // 更新归档缓存
}
cache = archivedCache; // 指向最终缓存数组
// 断言确保缓存上限至少为 127(符合 JLS 规范)
assert IntegerCache.high >= 127;
}
// 私有构造方法(防止外部实例化)
private IntegerCache() {}
}
如何提高性能?
通过享元模式,Integer.valueOf()
方法只会创建缓存范围内的对象,而不需要每次都new
一个新的Integer
对象。这一机制显著降低了内存开销,避免了不必要的垃圾回收。
三、面试题
以下代码会输出什么?
public class Main {
public static void main(String[] args) {
Integer i1 = new Integer(100);
Integer i2 = new Integer(100);
System.out.println(i1==i2); // false
}
}
注意:这里Integer是对象,==
比较的是对象的引用,而不是对象的内容。
public class Main {
public static void main(String[] args) {
Integer i1 = 100; // 自动装箱机制,在底层会自动调用静态方法valueOf的得到一个Integer对象
Integer i2 = 100;
Integer i3 = 200;
Integer i4 = 200;
System.out.println(i1==i2); // true
System.out.println(i3==i4); // false
}
}
补充:Integer的常用成员方法
注意:8中包装类中,除了Character
都有对应的parseXxx
的方法,进行类型转换