【Java知识点详解 10】装箱和拆箱

huatechinfo
发布于 2021-1-15 10:20
浏览
0收藏

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

在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】装箱和拆箱-鸿蒙开发者社区

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

上一小节了解装箱的基本概念之后,这一小节来了解一下装箱和拆箱是如何实现的。

  我们就以Interger类为例,下面看一段代码:

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


反编译class文件之后得到如下内容:

【Java知识点详解 10】装箱和拆箱-鸿蒙开发者社区

从反编译的字节码中可以看出,在装箱的时候自动调用的是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则是分别指向不同的对象。

其它的引用类型,可以去查看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类型与其他基本类型不能进行类型的转换(既不能进行自动类型的提升,也不能强制类型转换), 否则,将编译出错。

【Java知识点详解 10】装箱和拆箱-鸿蒙开发者社区

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

收藏
回复
举报
回复
    相关推荐