之前在IDEA中引入了阿里巴巴的Java规范插件,自动检测了一下开发中的项目,发现很多“不规范”的“常规”代码。然而,往往被我们忽视的常见代码,会隐藏着不规范的漏洞。
相信刚入职的Java开发者都遇到过这样的面试题:“说一下Java中==和equals的区别”。
我们先来复习一下,Java中的基本数据类型
Java中基本数据类型
上述Java中八种基本数据类型,对应的包装类型分别为:Boolean、Byte、Character、Short、Integer、Long、Float、Double。
将基本数据类型封装成对象的好处在于可以在对象中定义更多的功能方法操作该数据。
包装类之间相等判断的正确方式
-
包装类型间的相等判断应该用equals,而不是“==”;
警告提示.png -
原因分析:
原因.png
包装类型间的相等判断应该用equals,而不是'=='。
说明:对于Integer var=?在-128至127之间的赋值,Integer对象是在IntegerCache.cache产生,会复用已有对象,这个区间内的Integer值可以直接使用==进行判断,但是这个区间之外的所有数据,都会在堆上产生,并不会复用已有对象,这是一个大坑,推荐使用equals方法进行判断。
- 示例:
public class EqualsTest {
public static void main(String[] args) {
Integer a0 = 256;
Integer b0 = 256;
// 结果输出false:包装类型的相等比较应该用equals
// Integer范围:-128~127
System.out.println(a0==b0);
Integer a1 = -128;
Integer b1 = -128;
// 结果输出true:包装类型的相等比较应该用equals
// Integer范围:-128~127
System.out.println(a1==b1);
}
}
- 分析:
上述定义变量a0、b0等,实际体现在Integer源码中是调用valueOf(int i)方法
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
可见,当i的值在low和high范围内,直接取IntegerCache.cache的常量池中得对象。如果在范围之外,则直接new一个Integer新的对象。
查看一下Integer源码中IntegerCache部分:
/**
* Cache to support the object identity semantics of autoboxing for values between
* -128 and 127 (inclusive) as required by JLS.
*
* The cache is initialized on first usage. The size of the cache
* may be controlled by the {@code -XX:AutoBoxCacheMax=<size>} option.
* During VM initialization, java.lang.Integer.IntegerCache.high property
* may be set and saved in the private system properties in the
* sun.misc.VM class.
*/
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() {}
}
可见,IntegerCache中范围为-128到127,在这个范围内的对象,直接从cache[],Integer常量池中获取。
如果想控制范围大小,则需要通过-XX:AutoBoxCacheMax=<size>设置。
我们进一步查看一下JDK中Integer源码中equals方法:
public boolean equals(Object obj) {
if (obj instanceof Integer) {
return value == ((Integer)obj).intValue();
}
return false;
}
可见,Integer重写了equals方法,通过equals比较的是Integer包装类的int值。
附:
所有整数类型的类都有类似的缓存机制:
Byte 有 ByteCache 用于缓存 Byte 对象;
Short 有 ShortCache 用于缓存 Short 对象;
Long 有 LongCache 用于缓存 Long 对象。
Byte,Short,Long 的缓存池范围默认都是: -128 到 127。在日常项目开发中,我们往往喜欢使用==进行比较,因为日常开发中封装在实体类中的数值通常范围都在-128-127范围之内,所以基本没出现过异常,但是为了更好、更规范、更严谨的使用Java,还是需要使用equals比较。
总结:
1.==的比较(基本数据类型,比较的是值是否相等;非基本数据类型,即引用类型,比较的是对象指向的内存地址是否一致,即同一对象。)
2.而equals的比较(如果没有对equals方法进行重写,则比较的是引用类型的变量所指向的对象的地址;包装类型中分别都对equals方法进行了重写,只比较值是否相等。)