1、什么是装箱?什么是拆箱?
Java为每种基本数据类型都提供了对应的包装器类型,在Java 1.5之前如果要生成一个数值为10的Integer对象,必须这样创建:
Integer i = new Integer(10);
而在从Java1.5开始就提供了自动装箱的特性,如果要生成一个数值为10的Integer对象,只需要这样就可以了:
Integer i = 10;
这个过程会自动根据数值创建对应的Integer对象,这就是装箱。
那什么是拆箱呢?顾名思义,跟装箱对应的就是自动将包装器类型转换为基本数据类型:
Integer i = 10; //装箱
int n = i; //拆箱
简单一点说,装箱就是自动将基本数据类型转换为包装器类型;拆箱就是自动装包装器类型转换为基本数据类型。
下面简单介绍下基本数据类型以及所对应的包装器类型:
基础数据类型 | 包装器类型 |
---|---|
int(4字节) | Integer |
byte(1字节) | Byte |
short(2字节) | Short |
long(8字节) | Long |
float(4字节) | Float |
double(8字节) | Double |
char(2字节) | Character |
boolean | Boolean |
2、装箱和拆箱是如何实现的
Integer i = 10;
int n = i;
感兴趣的可以反编译class文件之后看到,在装箱的时候自动调用的是Integer的valueOf(int) 方法。而在拆箱的时候自动调用的是Integer的intValue方法。其它的数据类型也有相关的xxValue等方法。因此可以用一句话总结装箱和拆箱的实现过程:
装箱过程就是通过调用包装器的valueOf()方法实现的,而拆箱过程是通过调用包装器的xxValue方法实现的。
自动装箱拆箱机制其实是编译时自动完成替换的。
3、相关面试题
3.1下面这段代码的输出结果是什么?
Integer i1 = 100;
Integer i2 = 100;
Integer i3 = 150;
Integer i4 = 150;
System.out.print(i1 == i2); // 1
System.out.print(i3 == i4); //2
答:注释1返回true,注释2返回false。输出结果表明i1和i2指向的是同一个对象,而i3和i4指向的是不同的对象。至于为什么会有这个结果我们看下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);
}
从上面源码可以看出,在通过valueOf方法创建Integer对象的时候,如果数值在[-128,127]之间,便会返回指向IntegerCache.cache中已经存在的对象引用,否则创建一个新的Integer对象。上面的代码中i1和i2的值都在这个区间,所以会直接从cache中取已经存在的对象,所以i1和i2指向同一个对象,而i3和i4则分别指向不同的对象
3.2下面这段代码的输出结果是什么?
Double d1 = 100.0;
Double d2 = 100.0;
Double d3 = 150.0;
Double d4 = 150.0;
System.out.print(d1 == d2); //1
System.out.print(d3 == d4); //2
答:注释1返回false,注释2返回false。具体原因可以查看下Double类的valueOf实现。
public static Double valueOf(double d) {
return new Double(d);
}
至于为什么Double的valueOf会采用和Integer的valueOf方法不同的实现。很简单:在某个范围内的整型数值的个数是有限的,而浮点数不是。
注意:Integer、Short、Byte、Character、Long这几个类的valueOf方法实现是类似的。Double和Float的valueOf方法的实现是类似的。
3.3下面这段代码的输出结果是什么?
Boolean b1 = true;
Boolean b2 = true;
Boolean b3 = false;
Boolean b4 = false;
System.out.print(b1 == b2); //1
System.out.print(b3 == b4); //2
答:注释1返回true,注释2返回true。我们可以看下Boolean的valueOf
public static Boolean valueOf(boolean b) {
return b ? Boolean.TRUE : Boolean.FALSE;
}
其中TRUE和FALSE分别为在Boolean中定义的2个静态成员属性
public static final Boolean TRUE = new Boolean(true);
public static final Boolean FALSE = new Boolean(false);
所以通过上面可以明白为什么都会返回true。
3.4下面这段代码的输出结果是什么?
Integer i1 = 1;
Integer i2 = 2;
Integer i3 = 3;
Long g = 3L;
Long h = 2L;
System.out.print(i3 == (i1 + i2)); //true
System.out.print(i3.equals(i1 + i2)); //true
System.out.print(g == (i1 + i2)); //true
System.out.print(g.equals(i1 + i2)); //false
System.out.print(g.equals(i1 + h)); //true
答:本题需要有一点要注意:当“==”运算符的两个操作数都是包装器类型的引用,则是比较指向的是否是同一个对象,而如果其中有一个操作数是表达式(即包含算数运算)则比较的是数值(会触发自动拆箱过程)。对于包装器类型,equals方法并不会进行类型转换。
因此i3 == (i1 + i2)会返回true,因为触发拆箱之后比较的就是数值。
g == (i1 + i2)返回true,会触发自动拆箱,再触动自动装箱。
3.5 谈谈Integer i = new Integer(xxx) 和 Integer i = xxx 两种方式的区别
答:a、第一种方式不会触发自动装箱过程;而第二种方式会触发
b、在执行效率和资源占用上的区别。第二种方式的执行效率和资源占用在一般情况下要优于第一种
3.6 Integer i = 1; i += 1; 做了哪些事情?
答:首先 Integer i = 1; 做了自动装箱,接着 i += 1; 先将 Integer 类型的 i 自动拆箱成 int,完成加法运行之后的 i 再装箱成 Integer 类型。
3.7 java 是否存在使得语句 i > j || i <= j 结果为 false 的 i、j 值?
答:存在,java 的数值 NaN 代表 not a number,无法用于比较,例如使 i = Double.NaN; j = i; 最后 i == j 的结果依旧为 false。