拆箱装箱

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中的装箱和拆箱

你可能感兴趣的:(拆箱装箱)