Java自动装箱与拆箱

       在Java中的==与equals中说到编译器自动为我们加上valueOf这个方法,加上valueOf方法的过程,就是Java中经常说的装箱过程。

       Java中一共有四类八种基本数据类型,除掉这几种类型,其它的都是对象,也就是引用类型。在JDK1.5中,给这四类八种基本类型加入了包装类,对应如下:

类型 基本类型 包装类型
整型 byte Byte
short Short
int Integer
long Long
浮点型 float Float
double Double
逻辑型 boolean Boolean
字符型 char Character

       再看如下代码:

1
2
3
4
5
6
7
8
9
10
public static void main(String[] args) {
Integer integer1 = 100;//声明变量为Integer对象类型,并赋值为基本数据类型
int int1 = integer1;//声明变量为int值类型,并赋值为对象类型

Long long1 = 100L;//声明变量为Long对象类型,并赋值为基本数据类型
long l1 = long1;//声明变量为long值类型,并赋值为对象类型

Boolean aBoolean = true;//声明变量为Boolean对象类型,并赋值为基本数据类型
boolean bool = aBoolean;//声明变量为boolean值类型,并赋值为对象类型
}

       编译后打开class文件:

1
2
3
4
5
6
7
8
9
10
public static void main(String[] args) {
Integer integer1 = Integer.valueOf(100);//对int类型的值100加上了valueOf方法进行了装箱
int int1 = integer1.intValue();//对象类型integer1调用了intValue进行了拆箱

Long long1 = Long.valueOf(100L);//对long类型的值100L加上了valueOf方法进行了装箱
long l1 = long1.longValue();//对象类型long1调用了longValue进行了拆箱

Boolean aBoolean = Boolean.valueOf(true);//对boolean类型的值true加上了valueOf方法进行了装箱
boolean bool = aBoolean.booleanValue();//对象类型aBoolean调用了booleanValue进行了拆箱
}

       可以看出来,当我们变量声明为对象类型而赋值为基本数据类型时,Java编译器会对我们的基本数据类型进行装箱,而我们的变量声明为基本类型赋值为对象类型时,编译器又会对我们的对象类型进行拆箱处理。用valueOf作为装箱方法,拆箱方法就各自表述,一般都是基本数据类型加上Value做为拆箱方法,如intValue,longValue,booleanValue等等。

       将int的变量转换成Integer对象,这个过程叫做装箱,反之将Integer对象转换成int类型值,这个过程叫做拆箱。以上这些装箱拆箱的方法是在编译成class文件时自动加上的,不需要程序员手工介入,因此又叫自动装箱/拆箱。

       已经有了基本类型,为什么还要用包装类?

  • 对象是对现实世界的模拟(一切事物皆对象,通过面向对象的方式,将现实世界的事物抽象成对象),在现实中,假设我们去一个系统(数据库)里查询学生李四的年龄,如下图:

装箱与拆箱

       这时候,录入员还没给李四录入年龄这一项,如果我们用int来声明年龄,大家都知道int是要初始化的,默认情况下为0,0是什么意思,没出生吗?(当然也可以用-1来表示未录入,但总感觉有点怪怪的),如果用Integer来表示,就没这个问题了,为null,就是未录入。

  • 为泛型提供了支持:
1
2
3
4
public static void main(String[] args) {
List<Integer> list1 = new ArrayList<>();//泛型必须是一个对象
List<int> list2 = new ArrayList<>();//报错,不支持
}
  • 提供了丰富的属性和API:
public static void main(String[] args) {
    System.out.println(Integer.MAX_VALUE);//打印int能表示的最大值
    System.out.println(Integer.MIN_VALUE);//打印int能表示的最小值
    System.out.println(Integer.max(12, 13));//找出传入参数的大值
    System.out.println(Integer.min(12, 13));//找出传入参数的小值
    System.out.println(Integer.sum(12, 13));//求和
    System.out.println(Integer.valueOf("123"));//转换字符串为Integer类型
    System.out.println(Integer.compare(12, 54));//比较参数大小,返回-1,代表第一个参数小于第二个参数
    /**
     * 这种方式是可以写的,因为会在编译时解包处理后比较值 new Integer(180).intValue() < new Integer(180).intValue()
     */
    System.out.println(new Integer(180) < new Integer(180));
    System.out.println(new Integer(180).equals(new Integer(180)));//比较是否相等,推荐写法equals
    System.out.println(new Integer(180) == new Integer(180));//==是比较两个对象指向的引用是否一致,不能用来比较值是否相等,此处会返回false
}

       下面分析一下不同的声明方式在内存中的展现,代码如下:

public static void main(String[] args) {
    int int1 = 180;
    Integer int2 = new Integer(100);
}

       表现如下图:

int和Integer类内存展现

参考资料:
清浅池塘 Java自动装箱/拆箱

Fork me on GitHub