1、什么是装箱?什么是拆箱?面试
在Java SE5以前,若是要生成一个数值为10的Integer对象,必须这样进行:.net
Integer i = new Integer(10);
而在从Java SE5开始就提供了自动装箱的特性,若是要生成一个数值为10的Integer对象,只须要这样就能够了:code
Integer i = 10;
这个过程当中会自动根据数值建立对应的 Integer对象,这就是装箱。对象
那什么是拆箱呢?顾名思义,跟装箱对应,就是自动将引用类型转换为基本数据类型:blog
Integer i = 10; //装箱 int n = i; //拆箱
简单一点说,装箱就是 自动将基本数据类型转换为引用类型;拆箱就是自动将引用类型转换为基本数据类型。get
下表是基本数据类型对应的引用类型:源码
2、装箱和拆箱是如何实现的编译
上一小节了解装箱的基本概念以后,这一小节来了解一下装箱和拆箱是如何实现的。class
咱们就以Interger类为例,下面看一段代码:变量
public class Main { public static void main(String[] args) { Integer i = 10; int n = i; } }
反编译class文件以后获得以下内容:
从反编译的字节码中能够看出,在装箱的时候自动调用的是interger的valueOf(int)方法。而在拆箱的时候自动调用的是interger的intValue方法
所以用一句话总结装箱和拆箱的实现过程:
装箱过程是经过调用包装器的valueOf方法实现的,而拆箱过程是经过引用类型调用xxxValue实现的。
3、面试中的相关问题
虽然大多数人对装箱和拆箱的概念都清楚,可是在面试和笔试中遇到了与装箱和拆箱的问题却不必定会答得上来。下面列举一些常见的与装箱/拆箱有关的面试题。
一、下面这段代码的输出结果是什么?
public class Main { public static void main(String[] args) { Integer i1 = 100; Integer i2 = 100; Integer i3 = 200; Integer i4 = 200; System.out.println(i1==i2); System.out.println(i3==i4); } }
也许有些朋友会说都会输出false,或者也有朋友会说都会输出true。可是事实上输出结果是:
true false
为何会出现这样的结果?输出结果代表i1和i2指向的是同一个对象,而i3和i4指向的是不一样的对象。此时只需一看源码便知究竟,下面这段代码是Integer的valueOf方法的具体实现:
public static Interger valueOf(int i){ if(i>=-128&&i<=IntergerCache.high){ return IntergerCache.cache[i+128]; }else{ return new Interger(i); } }
经过valueOf方法建立Integer对象的时候,若是数值在[-128,127]之间,便返回指向IntegerCache.cache中已经存在的对象的引用;不然建立一个新的Integer对象。
上面的代码中i1和i2的数值为100,所以会直接从cache中取已经存在的对象,因此i1和i2指向的是同一个对象,而i3和i4则是分别指向不一样的对象。
其它的引用类型,能够去查看valueOf的实现。
******************************************************************************
弦外音:八种数据类型取值范围
整型:
byte:-2^7 ~ 2^7-1,即-128 ~ 127。1字节。Byte。末尾加B
short:-2^15 ~ 2^15-1,即-32768 ~ 32767。2字节。Short。末尾加S
int:-2^31 ~ 2^31-1,即-2147483648 ~ 2147483647。4字节。Integer。
long:-2^63 ~ 2^63-1,即-9223372036854774808 ~ 9223372036854774807。8字节。Long。末尾加L。(也能够不加L)
浮点型:
float:4字节。Float。
double:8字节。Double。
字符型:
char:2字节。Character。
布尔型:
boolean:Boolean。
类型转换:
boolean类型与其余基本类型不能进行类型的转换(既不能进行自动类型的提高,也不能强制类型转换), 不然,将编译出错。
byte型不能自动类型提高到char,char和short直接也不会发生自动类型提高(由于负数的问题),同时,byte固然能够直接提高到short型。
当对小于int的数据类型(byte, char, short)进行运算时,首先会把这些类型的变量值强制转为int类型进行计算,最后会获得int类型的值。所以,若是把2个short类型的值相加,最后获得的结果是int类型,若是须要获得short类型的结果,就必须显示地运算结果转为short类型。
******************************************************************************
下面程序的输出结果是什么?
public class Main { 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);//true System.out.println(e==f);//false System.out.println(c==(a+b));//true System.out.println(c.equals(a+b));//true System.out.println(g==(a+b));//true System.out.println(g.equals(a+b));//false System.out.println(g.equals(a+h));//true } }
当 "=="运算符的两个操做数都是 包装器类型的引用,则是比较指向的是不是同一个对象,而若是其中有一个操做数是表达式(即包含算术运算)则比较的是数值(即会触发自动拆箱的过程)。另外,对于包装器类型,equals方法并不会进行类型转换。
第一个和第二个输出结果没有什么疑问。第三句因为 a+b包含了算术运算,所以会触发自动拆箱过程(会调用intValue方法),所以它们比较的是数值是否相等。而对于c.equals(a+b)会先触发自动拆箱过程,再触发自动装箱过程,也就是说a+b,会先各自调用intValue方法,获得了加法运算后的数值以后,便调用Integer.valueOf方法,再进行equals比较。同理对于后面的也是这样,不过要注意倒数第二个和最后一个输出的结果(若是数值是int类型的,装箱过程调用的是Integer.valueOf;若是是long类型的,装箱调用的Long.valueOf方法)。