装箱和拆箱

212 阅读4分钟

一、什么是装箱?什么是拆箱?

Java SE5之前,如果要生成一个数值为10的Integer对象,必须这样进行:

Integer i = new Integer(10);

而在从Java SE5开始就提供了自动装箱的特性,如果要生成一个数值为10的Integer对象,只需要这样就可以了:

Integer i = 10;

这个过程中会自动根据数值创建对应的 Integer对象,这就是装箱。 那什么是拆箱呢?顾名思义,跟装箱对应,就是自动将引用类型转换为基本数据类型:

Integer i = 10; //装箱 
int n = i; //拆箱

简单一点说,装箱就是  自动将基本数据类型转换为引用类型;拆箱就是自动将引用类型转换为基本数据类型。

下表是基本数据类型对应的引用类型:【Java知识点详解 10】装箱和拆箱-OpenHarmony技术社区

二、装箱和拆箱如何实现的?

上一小节了解装箱的基本概念之后,这一小节来了解一下装箱和拆箱是如何实现的。 我们就以Interger类为例,下面看一段代码:

public class Main {
    public static void main(String[] args) {
         
        Integer i = 10;
        int n = i;
    }
}

反编译class文件之后得到如下内容: 【Java知识点详解 10】装箱和拆箱-OpenHarmony技术社区 从反编译的字节码中可以看出,在装箱的时候自动调用的是interger的valueOf(int)方法。而在拆箱的时候自动调用的是interger的intValue方法

因此用一句话总结装箱和拆箱的实现过程:

装箱过程是通过调用包装器的valueOf方法实现的,而拆箱过程是通过引用类型调用xxxValue实现的。

三、面试中的相关问题

虽然大多数人对装箱和拆箱的概念都清楚,但是在面试和笔试中遇到了与装箱和拆箱的问题却不一定会答得上来。下面列举一些常见的与装箱/拆箱有关的面试题。 1、下面这段代码的输出结果是什么?

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则是分别指向不同的对象。
不同包装类缓存范围: IMG_43A0D5A0E911-1.jpeg 下面程序的输出结果是什么?

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方法)。