Java 拆箱装箱

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。

你可能感兴趣的:(Java 拆箱装箱)