拆箱装箱

Java为每种基本数据类型都提供了对应的包装器类型。
Java语言是一个面向对象的语言,但是Java中的基本数据类型却是不面向对象的,这在实际使用时存在很多的不便,为了解决这个不足,在设计类时为每个基本数据类型设计了一个对应的类进行代表,这样八个和基本数据类型对应的类统称为包装类(Wrapper Class):

基本数据类型 包装器类型
int(4字节) Integer
byte(1字节) Byte
short(2字节) Short
long(8字节) Long
float(4字节) Float
double(8字节) Double
char(2字节) Character
boolean(未定) Boolean
为什么自动装箱拆箱 基本数据类型不是对象,也就是使用int、double、boolean等定义的变量、常量。
基本数据类型没有可调用的方法。
int t = 1; t. 后面没有方法
Integer t =1; t. 后面就有很多方法可调用
什么是自动装箱拆箱
  • 自动装箱: 自动将基本数据类型转换为包装器类型
  • 自动拆箱: 自动将包装器类型转换为基本数据类型
装箱 Java SE5之前
一般我们要创建一个类的对象实例的时候,我们会这样:
Class a = new Class(parameter);

在Java SE5之前,如果要生成一个数值为10的Integer对象,必须这样进行:
Integer i = new Integer(10);

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

注意:不是 int i = 100;
实际上,执行上面那句代码的时候,系统为我们执行了:
Integer i = Integer.valueOf(10);

此即基本数据类型的自动装箱功能。
拆箱 那什么是拆箱呢?顾名思义,跟装箱对应,就是自动将包装器类型转换为基本数据类型:
Integer i = 10; //装箱 int n = i; //拆箱

底层怎么实现 Integer在装箱的时候自动调用的是Integer的valueOf(int)方法。而在拆箱的时候自动调用的是Integer的intValue方法。
因此可以用一句话总结装箱和拆箱的实现过程:
  • 装箱过程是通过调用包装器的valueOf()实现的
  • 拆箱过程是通过调用包装器的 xxxValue()实现的。(xxx代表对应的基本数据类型)。
一些运用 Integer.valueOf(int i) “==”和“equal”的区别:
类型 == equals
字符串变量 对象在内存中的首地址 字符串内容
非字符串变量 对象在内存中的首地址 对象在内存中的首地址
基本类型 不可用
包装类 地址 内容
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); //true System.out.println(i3==i4); //false } }

结果:
true false

输出结果表明 i1 和 i2 指向的是同一个对象,而 i3 和 i4 指向的是不同的对象。
看源码,下面这段代码是Integer的valueOf方法的具体实现:
public static Integer valueOf(int i) { if(i >= -128 && i <= IntegerCache.high) return IntegerCache.cache[i + 128]; else return new Integer(i); }

【拆箱装箱】可以看出,在通过valueOf方法创建Integer对象的时候
  • 如果数值在 [-128,127] 之间,便返回指向IntegerCache.cache中已经存在的对象的引用
  • 否则创建一个新的Integer对象
上面的代码中 i1 和 i2 的数值为100,因此会直接从cache中取已经存在的对象,所以 i1 和 i2 指向的是同一个对象,而 i3 和 i4 则是分别指向不同的对象。
==与equals()
  • ==运算符的
    • 两个操作数都是 包装器类型的引用,则是比较指向的是否是同一个对象
    • 如果其中有一个操作数是表达式(即包含算术运算)则比较的是数值(即会触发自动拆箱的过程)
  • 对于包装器类型,equals()并不会进行类型转换
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 } }

true false true true true false true

  • c==de==f分析见上一节
  • 第三句由于 a+b 包含了算术运算,因此会触发自动拆箱过程(会调用intValue方法),因此它们比较的是数值是否相等。
  • c.equals(a+b)
  1. a+b触发拆箱,会先各自调用intValue方法
  2. 得到了加法运算后的数值之后
  3. 触发装箱过程,调用Integer.valueOf方法
  4. 进行equals()比较
  • g.equals(a+b)
  1. a+b触发拆箱,会先各自调用intValue方法
  2. 得到了加法运算后的数值,但类型还是int
  3. 触发装箱过程
  4. 进行equals()比较,Integer与Long 类型不同,false
  • g.equals(a+h)
  1. 先自动触发拆箱,就变成了int类型和long类型相加
  2. 这个会触发类型晋升,结果是long类型的
  3. 然后会触发装箱过程,就变成Long了
总结:
算数运算触发自动拆箱
注意不同类型触发类型转换
参考文章
JAVA基本类型和包装类
深入剖析Java中的装箱和拆箱

    推荐阅读