探秘Java的自动装箱与拆箱

Java提供了8种基本数据类型:byte、short、int、long、char、boolean、float和double,然而它们却不能像引用类型那样向上转型为Object,也无法使用泛型。因此,Java提供了它们对应的引用类型包装类:Byte、Short、Integer、Long、Character、Boolean、Float和Double

什么是自动装箱与自动拆箱

Java 5开始就提供了自动装箱/拆箱的特性,装箱就是将基本数据类型转换为它的包装类,而拆箱就是将包装类转换为基本数据类型。

// 自动装箱
Integer a = 1;
// 自动拆箱
int b = a; 

自动装箱/拆箱的实现原理

我们使用javac编译上面的代码,然后使用javap来查看一下字节码,可以看到这么一部分

0: iconst_1
1: invokestatic  #2                  // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
4: astore_1
5: aload_1
6: invokevirtual #3                  // Method java/lang/Integer.intValue:()I
9: istore_2
10: return

自动装箱调用了包装类的valueOf方法,而自动拆箱调用了xxxValue方法,就是这么简单

包装类缓存

观察如下代码,思考一下输出结果

public class Main {
    public static void main(String[] args) {
        Integer a = 1;
        Integer b = 1;
        Integer c = 233;
        Integer d = 233;
        System.out.println(a == b);
        System.out.println(c == d);
    }
}

返回两个true?还是返回两个false?

正确答案是true false。是不是有点出乎意料?我们进入Integer.valueOf()方法的源码来一探究竟。

valueOf的实现

public static Integer valueOf(int i) {
    if (i >= IntegerCache.low && i <= IntegerCache.high)
        return IntegerCache.cache[i + (-IntegerCache.low)];
    return new Integer(i);
}

我们看到,它首先判断i值的范围,如果在IntegerCache.lowIntegerCache.high之间的话,就从名为IntegerCache.cache的缓存数组中返回包装类的对象。

private static class IntegerCache {
    static final int low = -128;
    static final int high;
    static final Integer cache[];

low的值为-128,而high的值一般默认为127。这就很好的解释了为什么1和1自动装箱的引用相等,而233和另一个233装箱后的引用不同。

其他包装类的valueOf

大家可以自行查看源代码,这里只做一个简单总结

  • byte:全部缓存,-128-127
  • short:缓存-128到127
  • int:缓存-128到high(默认127)
  • long:缓存-128到127
  • char:缓存0-127
  • boolean:使用名为TRUE和FALSE的缓存
  • float:不缓存
  • double:不缓存

装箱拆箱与运算的复杂情况

先下结论:当 ==运算符的两个操作数都是包装器类型的引用,则是比较是否是同一个实例,而如果其中有一个是基本数据类型比较数值(自动拆箱)。此外,对于包装器类型,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 -> Integer不是Long实例
        System.out.println(g.equals(a + h)); // true
    }
}

Azure99

底层码农,休闲音游玩家,偶尔写写代码

看看这些?

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注