面试题总结——包装类的自动拆装箱以及"缓存值"问题(常量池)
Java包装类的自动拆装箱可参考:深入理解Java中的包装类与自动拆装箱。
再多强调一下,在使用包装类的==与equals()时需要注意:
算术运算会触发自动拆箱过程
①若此时再使用==比较的是两个数值的大小,两个数值之间可以进行Java允许的类型转换。
②若此时在使用equals()比较又会触发自动装箱过程,但不会发生类型转换(已成包装类)
看看下面这个例子:
public static void main(String[] args) {
Integer a = 1;
Integer b = 2;
Integer c = 3;
Integer d = 3;
Integer e = 321;
Integer f = 321;
Long g = 3L;
Long h = 2L;
System.out.println(c==d);
System.out.println(e==f);
System.out.println(c==(a+b));
System.out.println(c.equals(a+b));
System.out.println(g==(a+b));
System.out.println(g.equals(a+b));
System.out.println(g.equals(a+h));
}
// 打印结果
true
false
true
true
true
false
true
解析一下这个例子,第1个与第2个由于"常量池"的存在就不多说(下面会详细讲解),
主要说一下3,4,5,6,7。
第3个,算数运算会触发自动拆箱过程,因此两个数值比较后返回true
第4个,算数运算会触发自动拆箱过程,equals()方法又会触发自动装箱过程,因此两个Integer包装类比较的就是其值。
第5个,算数运算会触发自动拆箱过程,因此两个数值比较后返回true(虽然一个是int一个是long,但是Java会自动类型转换
,1==1L的结果为true)
第6个,算数运算会触发自动拆箱过程,自动拆箱后的运算结果是一个int类型,而equals()方法又会触发自动装箱过程。但两个包装类的类型明显不同并且不能相互转换(类型不同,一个是Integer,一个是Long)
第7个,算数运算会触发自动拆箱过程,自动拆箱后的运算结果是一个long类型(int+long),此时equals()方法再触发自动装箱过程,此时两个Long包装类比较的就是其值(类型已经相同)
华丽的分割线
本篇文章主要探讨一下在Java的各包装类中出现的"缓存值"问题,按照Java语言的特性,没new一个对象会在堆空间中创建一块内存空间用于存储该对象的内容,而对于包装类来说,Java将经常出现的值包装到同一个对象中,也就是所谓的"常量池",下次再访问时直接将该引用指向"常量池"中已存在的常量
(请参考String中的"常量池"相关概念)
首先看一下Integer包装类valueOf()方法的源码:
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
在其源码中可以看到,如果数值在[-128,127]之间,便返回指向IntegerCache.cache中已经存在的对象的引用;否则才会创建一个新的Integer对象。因此对于Integer
来说,相当于其常量池中存储的数据为[-128,127]
。
正是由于Integer包装类所使用的"缓存值"技术,才出现下面代码的执行结果
public static void main(String[] args) {
Integer i1 = 100;
Integer i2 = 100;
Integer i3 = 1000;
Integer i4 = 1000;
System.out.println(i1==i2);
System.out.println(i3==i4);
}
// 打印结果为:
true
false
举一个相反的例子,看一下Double包装类valueOf()方法的源码:
public static Double valueOf(double d) {
return new Double(d);
}