Java装箱和拆箱

概念

简单说

装箱:装箱就是将基本数据类型转换为包装器类型,通过valueOf()函数实现装箱

拆箱:拆箱就是将包装器类型转换为基本数据类型,通过**×××Value()**函数实现拆箱

自动装箱:装箱中有一种非显示地装箱,即隐式装箱

自动拆箱:拆箱中有一种非显示地拆箱,即隐式拆箱

会发生装箱、拆箱的数据类型和包装类型

发生场景

可以先看原理,再回来自己分析场景

进行传参时,会发生自动装箱和拆箱

1
2
3
4
5
6
7
public static void main(String[] args){
fun(1);
}

public void fun(Integer n){ //会进行自动装箱
System.out.println(n);
}

包装类和基本类型的大小比较

  • 两个都为包装类型,比较的是地址(可能是一个对象,也有可能不是一个对象)
1
2
3
4
5
6
7
8
Integer a = 1;        
Integer b = 1;
Integer c = 888;
Integer d = 888;
System.out.println(a.equals(b)); //true,比较的是内容(包装类型比较建议使用equals)
System.out.println(a == b); //true,两个是同一个对象,存储在内存中的数组中,包装类型中==比较的是地址

System.out.println(c == d); //false,两个虽然数值上相等,但两个是两个对象,==比较的是地址
  • 两个都为基本数据类型,比较的是数值,并且数值不一样的会自动类型提升
1
2
3
int e = 888;
int f = 888;
System.out.println(e == f); //true,基本数据类型比较的是数值
  • 一个为包装类型,一个为基本数据类型,先进拆箱,转变为基本数据类型,再比较数值
1
2
Integer a = 1;
System.out.println(a == 1 ? "等于" : "不等于"); //等于

包装类型(和基本类型)的运算

1
2
3
4
5
6
7
8
int i = 1;
Integer j = 2; //自动装箱
j = j + i; //先自动拆箱,再自动装箱

int a = 888;
Integer c = 888;
Integer d = 1776;
System.out.println(d == (a + c)); //true,a+c先将c拆箱,得到基本类型,再将基本类型与包装类型比较,又将包装类型d拆箱,得到两个基础数据类型,两个基本类型进行数值比较

总结

当数据类型发生转变时,会发生装箱或者拆箱

当运算的时候,也会发生拆箱、再装箱

原理

不同数据类型的**×××Value()valueOf()**的底层实现不同

所以,下面以Integer为例:

Integer装箱

1
2
3
4
5
public class A {
public static void main(String[] args) {
Integer a = 1; //隐式装箱
}
}

①对生成的class类进行反编译

image-20211016175456093

可以看到调用了**Integer.valueOf()**方法,执行了装箱

②查看底层源码

image-20211016175855871

解释:在Integer装箱时,如果值在-128到127之间,则会从内存中的IntegerCache.cache[]数组中找到现成的数据,以供使用(这个数组中的数据是Integer类的static静态代码块中初始过的,所以程序一运行时,这些数据就在内存中存在了);反之,如果数值在-128到127之外,则会重新new一个Integer类型的对象出来(也就出现了地址值不一样的现象)

Integer拆箱

1
2
3
4
5
6
public class A {
public static void main(String[] args) {
Integer a = 1; //隐式装箱
int b = a; //隐式拆箱
}
}

①对生成的class类进行反编译

image-20211016180904308

可以看到,调用了**Integer.intValue()**方法,执行了拆箱

②查看底层源码

image-20211016181054704

image-20211016181437555

解释:可以看到,只返回了一个value,这个value其实是Integer的一个成员属性,value的值就是等于装箱的时候new Integer()时候的传入的int基本类型的值(数值),所以拆箱的时候,就是把包装类型的数据转换为基本类型数据

推广其他包装类

其他包装类和Integer类似,也都时通过是否new一个新的对象,来实现装箱、拆箱

装箱、拆箱目的

  1. 自动装箱、拆箱,有助于避免错误的发生:因为将包装类型的对象和基本数据类型进行转换时,自动装箱总是生成一个正确的对象,自动拆箱总是生成一个正确的值
  2. 有助于减少内存的浪费:例如Integer类的底层中有一个数组存放一定量的值

思考:装箱、拆箱的本质

装箱和拆箱的本质好像就是值类型和引用类型的互相转换。

装箱是值类型转换为引用类型;

拆箱就是引用类型转换为值类型。

tips:

java中除了int等基本数据类型是值类型外,其余所有的类型都称为引用类型(数组,类,接口,字符串)

Contents
  1. 1. 概念
    1. 1.1. 会发生装箱、拆箱的数据类型和包装类型
  2. 2. 发生场景
    1. 2.1. 进行传参时,会发生自动装箱和拆箱
    2. 2.2. 包装类和基本类型的大小比较
    3. 2.3. 包装类型(和基本类型)的运算
    4. 2.4. 总结
  3. 3. 原理
    1. 3.1. Integer装箱
      1. 3.1.1. ①对生成的class类进行反编译
      2. 3.1.2. ②查看底层源码
    2. 3.2. Integer拆箱
      1. 3.2.1. ①对生成的class类进行反编译
      2. 3.2.2. ②查看底层源码
    3. 3.3. 推广其他包装类
  4. 4. 装箱、拆箱目的
  5. 5. 思考:装箱、拆箱的本质
|