可以尝试以下两组比较的输出,看看是否符合预期答案:
第一组:
int a =127;
Integer b1=127;
Integer b2=127;
Integer c= new Integer(127);
System.out.println(a==b1);
System.out.println(a==c);
System.out.println(b1==c);
System.out.println(b1==b2);
第二组:
int aa =128;
Integer bb1=128;
Integer bb2=128;
Integer cc= new Integer(128);
System.out.println(aa==bb1);
System.out.println(aa==cc);
System.out.println(bb1==cc);
System.out.println(bb1==bb2);
答案揭晓:
第一组答案是
true
true
false
true
第二组答案是
true
true
false
false
其实如果只是想知道比较方法那很简单,我见过类似这样的总结(当然,这些结论都没错):
1.Integer与new Integer一定不会相等
2.两个都是非new出来的Integer,如果数在-128到127之间,则是true,否则为false
3.两个都是new出来的,则为false
4.int和integer(new或非new)比较,都为true
眼睛:我会了
脑子:不,你不会 !
整理一下思路:
问题1 :==比较的是什么?
地址。
不完全对,Java的数据类型可以分为基本数据类型(如int)和引用数据类型(如Integer),对于后者来说,双等号代表比较地址,而对于前者来说,双等号代表数值(value)比较。
基本数据类型 == 基本数据类型 比较值
引用数据类型 == 引用数据类型 比较地址
那么有了问题2,
问题2:基本数据类型==引用数据类型,比较的又是什么
当然是比较值了。。。
例如在一个方法当中,int a=1,这是一个局部变量,局部变量存储在哪里呢?栈当中(方法执行就是出栈和入栈的过程),而Integer呢?对象,当然在堆当中,不论如何也不会是同一个地址。当然这只是一方面的理解。
换个角度,int是基本数据类型,我要求比较值,Integer是引用数据类型,你想比较地址。总之我俩要么都是基本数据类型,要么都是引用数据类型才能进行比较。
Integer实际上包含了一个int类型的成员变量,可以把这个成员直接拿过来使用,所以相比int转化为Integer,Integer还是选择妥协,向下兼容,最终变成了比较值,所以有了“自动拆箱”这个词。
很好理解。。。
继续
问题3:两个直接赋值没有new的Integer怎么比较
(建议到这里先看看文末的补充内容)
没new也是引用类型,当然要比较地址
值在-128-127范围内的,如果值相同,就是同一个对象(值不同,地址当然更不同)
值不在-128-127范围内的,地址一定不同;
Integer b1=127;
Integer b2=127;
Integer bb1=128;
Integer bb2=128;
System.out.println(b1==b2);//true
System.out.println(bb1==bb2);//false
Integer a=1 ;
1明明是一个int类型,你用Integer去声明它,应该报错才对,但是为了更好的进行数据转换,Java允许你这么声明,同时会将它自动装箱为Integer。在这种情况下, 如果a的值在-128-127范围内,就直接从默认的缓存中获取地址。
那如果不在这个范围内呢?就会new一个Integer对象(对此有疑问的直接跳到文章末尾看),即使两次值都一样,也是new出了两个对象,地址是不同的。
综上,不new直接复制的Integer,只要在-128-127这个范围内的,如果值相同,就是同一个对象(相同地址),值不同,地址也一定不同。
问题4:两个new过的Integer怎么比较
两个对象,两个地址
不可能相同!!!
这个不作解释了。。。
问题5:直接赋值不new和new了赋值的怎么比较
例如:
Integer b1=127;
Integer c= new Integer(127);
前面讲到,不new的情况下(b1),如果值在-128-127范围内,会从默认缓存中直接获取引用(或者说地址),此时b1存放于常量池当中,如果值不在这个范围内,会new一个Integer对象并将其存放于堆当中。一个在常量池(方法区),一个在堆当中,地址是不可能相同的。
这是不new的情况,new了又是什么情况?
对比普通实体类,new一个对象会调用它的构造方法,开辟新的内存空间,我们刚才说不new直接赋值的情况下:
如果a的值在-128-127范围内,存放于常量池,而new出来对象在堆当中,地址肯定是不一样的;
如果a的值不在-128-127范围内,会new一个新的对象存放于堆当中,而直接new出来对象也在堆当中,但是这是两个new出的对象,地址一定是不一样的;故而说:
new过的Integer和没new过的Integer这两者用双等号比较,结果一定是false。
补充:
自动装箱:
Java 编译器把原始类型自动转换为封装类的过程称为自动装箱(autoboxing),这相当于调用 valueOf 方法
比如 Integer i=1;这就完成了自动装箱
来看看这个valueOf方法写了什么:
/**
* Returns an {@code Integer} instance representing the specified
* {@code int} value. If a new {@code Integer} instance is not
* required, this method should generally be used in preference to
* the constructor {@link #Integer(int)}, as this method is likely
* to yield significantly better space and time performance by
* caching frequently requested values.
*
* This method will always cache values in the range -128 to 127,
* inclusive, and may cache other values outside of this range.
*
* @param i an {@code int} value.
* @return an {@code Integer} instance representing {@code i}.
* @since 1.5
*/
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
这里的low和high的初始化之后的值分别是-128和127
Integer中的静态内部类 IntegerCache——缓存
IntegerCache默认缓存了-128-127范围的整型;
当执行Integer i = 一个值时,如果这个值在-128-127这个缓存的范围内,就直接引用缓存中对应的地址,如果值不在这个范围内,就会new integer()一个对象。