Java Integer
在比较中,其实都知道Integer是一种对象包装类,在程序使用中需要稍加注意就能避免绝大部分问题,在一次使用中因为使用 == (双等号) 判断导致程序运行出现偏差...😨😨😨
场景为:
某指定属性为Integer类型, 其中使用了 101 ,201 两个数值去表示A类、B类
当时A类101,并未出现问题,B类201总是无法按照预定程序执行。
于是调试了一下发现罪魁祸首 "==" 判断,恍然大悟...💡
==运算符
如果使用 == 运算符进行比较的话,由于检测的是对象是否指向同一个内存区域,由于初始化时的不确定性,比较的结果也可能不是我们想要的。如下所示:
Integer integer1 = new Integer(101);
Integer integer2 = new Integer(101);
Integer integer3 = 101;
Integer integer4 = 101;
Integer integer5 = 201;
Integer integer6 = 201;
// false 两个new的对象
System.out.println(integer1 == integer2);
// false integer1在堆中, integer3指向IntegerCache缓存(方法区中)
System.out.println(integer1 == integer3);
// true 都指向缓存中同一个对象
System.out.println(integer3 == integer4);
// false 超出缓存范围, 分别是两个new出来的对象
System.out.println(integer5 == integer6);
相信到这里,已经不难看出为什么201为什么出现运行偏差了
而《阿里Java开发手册》中有这样一项强制要求:
“所有整形包装类对象之间值的比较,全部使用equals方法比较。说明:对于Integer var= ?在-128到127范围内的赋值,Integer对象在IntegerCache.cache产生,会复用已有对象,这个区间的Integer值可以直接使用==进行判断,但是这个区间之外的所有数据都会在堆上产生,并不会复用已有对象,这是一个大坑,推荐使用equals方法进行判断。”
如果有些结果和我们预料的不太一样,我们还是认真深究一下。
不同创建形式的比较
// 自动装箱,会调用valueOf();
Integer i1 = 88;
// 手动装箱 与直接赋值操作效果一致
Integer i2 = Integer.valueOf(88);
// 创建新的类
Integer i3 = new Integer(88);
System.out.println(i2 == i3);
通过new和valueOf创建的是完全两个对象,那么 i2 == i3 项,直接比较两个对象的引用肯定是不相等的,因此结果为false。
Integer直接赋值和valueOf是等效的,那先看一下valueOf及相关的方法。
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 {
// high value may be configured by property
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);
// Maximum array size is Integer.MAX_VALUE
h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
} catch( NumberFormatException nfe) {
// If the property cannot be parsed into an int, ignore it.
}
}
high = h;
cache = new Integer[(high - low) + 1];
int j = low;
for(int k = 0; k < cache.length; k++)
cache[k] = new Integer(j++);
// range [-128, 127] must be interned (JLS7 5.1.7)
assert IntegerCache.high >= 127;
}
private IntegerCache() {}
}
valueOf方法判断数字是否大于low(-128)并且小于high(127),如果满足条件,则直接从IntegerCache中返回对应数字。
IntegerCache用于存储一些常用的数,防止重复创建,在Integer类装入内存时通过静态代码进行初始化。
所以只要是用valueOf或者Integer直接赋值的方式创建的对象,其值小于127且大于-128的,无论对其进行==比较还是equals 比较,都是true。
上面的源码及原理也解释了阿里Java开发手册中所说明的原因。
public boolean equals(Object obj) {
if (obj instanceof Integer) {
return value == ((Integer)obj).intValue();
}
return false;
}
Integer类重写了object的equals方法,调用时实际比较的是两个对象的值,和对象存储在哪里没有关系。
equals实现比较简单,先比较类型是否一致,如果不一致,直接返回false;否则,再比较两者的值,相同则返回true。
所以为了保持对象之间比较结果的一致性,同时我们进行比较的初衷应该也是比较它们之间的值,所以使用equals方法
FINALLY
关于Integer的比较核心点有以下三点:引用对象的存储结构、Integer的缓存机制、自动装箱与拆箱。
Integer在==运算时,总结一下:
(1)如果==两端有一个是基础类型(int),则会发生自动拆箱操作,这时比较的是值。
(2)如果==两端都是包装类型(Integer),则不会自动拆箱,首先会面临缓存问题,即便在缓存范围内的数据还会再次面临创建方式的问题,因此强烈建议使用equals方法进行比较。