深入解析Java中的“128陷阱“:Integer缓存机制源码分析
目录
一、现象展示
二、源码解析
三、关键点分析
关键点分析
为什么叫"128陷阱"
正确的比较方式(超出缓存范围外的数值)
其他包装类的缓存
四、延伸(关于equals&==哪个更加权威)
“128陷阱”是Java中的一道经典面试题,其中涉及Integer中的缓存机制知识,下面从源码角度解析一下这个问题。
一、现象展示
请仔细查看下面的两段代码:
不难发现看似很正确的等价判断却得到了截然相反的结果。
Integer a = 127;
Integer b = 127;
System.out.println(a == b); // trueInteger c = 128;
Integer d = 128;
System.out.println(c == d); // false
二、源码解析
public static Integer valueOf(int i) {if (i >= IntegerCache.low && i <= IntegerCache.high)return IntegerCache.cache[i + (-IntegerCache.low)];return new Integer(i);
}private static class IntegerCache {static final int low = -128; // 缓存范围的最小值static final int high; // 缓存范围的最大值static final Integer cache[]; // 缓存数组static {// 静态初始化块,用于初始化缓存范围和缓存数组int h = 127; // 默认缓存范围的最大值String integerCacheHighPropValue =sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");if (integerCacheHighPropValue != null) {try {int i = parseInt(integerCacheHighPropValue); // 尝试解析配置属性i = Math.max(i, 127); // 确保缓存范围至少为 127// 最大缓存范围受限于数组大小限制h = Math.min(i, Integer.MAX_VALUE - (-low) -1);} 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++); // 填充缓存数组// 确保缓存范围至少包含 [-128, 127],这是 Java 语言规范的要求assert IntegerCache.high >= 127;}private IntegerCache() {} // 私有构造函数,防止外部实例化
}
三、关键点分析
关键点分析
-
缓存范围:默认缓存-128到127之间的Integer对象
-
下限固定为-128
-
上限默认为127,但可通过JVM参数
-XX:AutoBoxCacheMax=<size>
调整
-
-
自动装箱机制:当使用
Integer i = 127
语法时,实际上调用的是Integer.valueOf(127)
-
缓存实现:
-
在Integer类初始化时,会预先创建好缓存范围内的Integer对象
-
当调用valueOf()时,如果在缓存范围内,直接返回缓存对象
-
超出范围则新建Integer对象
-
为什么叫"128陷阱"
-
对于-128到127之间的值,
valueOf()
返回的是缓存中的同一个对象,所以==
比较为true -
对于超出这个范围的值,每次
valueOf()
都会创建新对象,==
比较为false -
而使用
equals()
方法比较时,比较的是实际int值,所以总是正确
正确的比较方式(超出缓存范围外的数值)
Integer a = 128;
Integer b = 128;
System.out.println(a.equals(b)); // true,正确比较内容
System.out.println(a.intValue() == b.intValue()); // true,拆箱比较
其他包装类的缓存
类似的缓存机制也存在于其他包装类中:
-
Byte: 全部缓存(-128~127)
-
Short: -128~127
-
Long: -128~127
-
Character: 0~127
-
Boolean: TRUE和FALSE两个实例
四、延伸(关于equals&==哪个更加权威)
- 比较两个对象的内容是否相等,应该使用 equals 方法。
- 比较两个引用是否指向同一个对象实例,或者比较基本数据类型的值是否相等,应该使用 == 运算符。
- 在实际开发中,equals 更常用于比较对象的内容,因为它可以通过重写来实现更灵活和合理的逻辑。 因此,在比较对象的内容时,equals 是更权威的选择。
【注:】重写equals方法一定要重写其中的hashcode方法。