本文来自:https://blog.youkuaiyun.com/weixin_42921327/article/details/115518328
大神写得详细又准确,我认为再无修改的必要了
但为了防止链接失效,在此附上原文:
=========================================================================
包装类及基本数据类型之间大小比较
一、例子
public class IntegerTest {
public static void main(String[] args) {
Integer a = 127;
Integer b = 127;
System.out.println(a == b); //true
a = 128;
b = 128;
System.out.println(a == b); //false
a = -128;
b = -128;
System.out.println(a == b); //true
a = -129;
b = -129;
System.out.println(a == b); //false
}
}
二、源码分析
从JDK1.5就开始引入了自动拆装箱的语法功能,也就是系统将自动进行基本数据类型和与之相对应的包装类型之间的转换,这使得程序员书写代码更加方便。
1、装箱过程是通过调用包装器的valueOf方法实现的。
2、拆箱过程是通过调用包装器的xxxValue方法实现的(xxx表示对应的基本数据类型)。
当给a赋值时,实际上是调用了Integer.valueOf(int i)方法进行装箱,由于Integer的缓存为[-128,127],通过源码可以看出,当数值范围为[-128,127]时,会从缓存区取到Integer数据,而不再这个范围内的数值会重新 new 新对象。引用数据类型的== 比较的是地址值,[-128,127]范围内的包装类型都是从缓存区中取到,地址值相同,所以为true。
其JDK 8源码如下:
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
IntegerCache源码:
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() {}
}
当赋的基本数据类型值不在[-128, 127]之间,会去Java堆内存中new一个对象出来,显然它们是两个不同的对象,所以结果false;
而值在[-128, 127]之间,会直接从IntegerCache中获取,也就是从缓存中取值,不用再创建新的对象,即同一个对象,所以结果true。
关系操作符“== ”生成的是一个boolean结果,它们计算的是操作数的值之间的关系。如果是基本类型则直接判断其值是否相等,如果是对象则判断是否是同一个对象的引用,即其引用变量所指向的对象的地址是否相同。
我们来看下Integer类中equals源码:
public boolean equals(Object obj) {
if (obj instanceof Integer) {
return value == ((Integer)obj).intValue();
}
return false;
}
使用equals()方法比较大小会通过intValue()方法拆箱,即将包类型转换为基本数据类型。所以equals方法比较的是它们的值了。
三、其他示例
@Test
public void test(){
Short packShort=18;
Short packShort1 = 18;
short baseShort=18;
Integer packInt=18;
int baseInt=18;
//用Short的equals()方法与short进行比较
System.out.println(packShort.equals(baseShort)); //true
//用Short的equals()方法与Short进行比较
System.out.println(packShort.equals(packShort1)); //true
//用Short的equals()方法与int进行比较
System.out.println(packShort.equals(baseInt)); //始终为false
//用Short的equals()方法与Integer进行比较
System.out.println(packShort.equals(packInt)); //始终为false
//用==比较Short与Short
System.out.println(packShort == packShort1); // [-128,127]为true,其他范围数值比较为false
//用==比较Short和Integer,不能进行比较,类型不同,编译不通过
//System.out.println(packShort==packInt);
//用==比较short和int
System.out.println(baseShort == baseInt); // true
//用==比较Short和short
System.out.println(packShort == baseShort); //true
//用==比较Short和int
System.out.println(packShort == baseInt); //true
System.out.println(packInt == baseShort); //true
}
分析:
比较Short和short:
1)使用equals()方法进行比较,short类型会被判断为是Short类型的实例,然后两个对象都会被转化为基本类型用 == 进行比较,所以结果为true。
2)用 ==进行比较,Short类型对象被拆箱(转为short基本类型),所以结果为true。
比较Short和int
1)用Short的equals()方法与int进行比较的时候,由于类型判断那里就已经为false了,返回结果始终为false。
2)用==比较Short和int的时候,Short首先是进行了拆箱(转为short基本类型),然后是自动提高类型(转为int),之后才进行比较,所以结果为true。
用Short的equals()方法与Integer进行比较的时候,与用Short的equals()方法与int进行比较的时候同样的原因,返回结果始终为false。
总结:
1、使用equals()方法
两种数据类型(包装类和对应基本数据类型为同一种类型)必须相同,否则比较出来的结果始终为false,因为equals()通过拆箱为基本数据类型进行比较大小,推荐使用;
2、使用==
两个数据都是包装类型,则包装类型必须相同(否则编译不通过),且比较的是地址值,需要注意这两个包装类的来源,是否从缓存区中获取;
两个数据都是基本数据类型,则比较数值是否相等,并且类型不一样会进行自动类型提升
一个为基本数据类型,一个为包装类型时,包装类型对象被拆箱转为基本类型,然后按照两个基本数据类型的方式进行比较
阿里巴巴Java开发手册说明:
【强制】所有的相同类型的包装类对象之间值的比较,全部使用equals方法比较。 说明:对于Integer var = ? 在-128 至 127范围内的赋值,Integer对象是在IntegerCache.cache产生,会复用己有对象,这个区间内的Integer值可以直接使用==进行判断,但是这个区间之外的所有数据,都会在堆上产生,并不会复用己有对象,这是一个大坑,推荐使用equals方法进行判断。
四、缓存范围
注:两个同类型的Float或Double类型的==比较永远都是返回false。