Java 的自动拆箱和装箱是Java语言的一颗语法糖,但如果没有经过深入的分析与思考,我们不可避免地会犯许多错误。请看下面的一段代码:

public class Test {
    public static void main(String[] args) {
        int num1 = 1;
        int num2 = 1;

        System.out.println(num1 == num2);

        Integer num3 = 1;
        Integer num4 = 1;

        System.out.println(num3 == num4);

        Integer num5 = 128;
        Integer num6 = 128;

        System.out.println(num5 == num6);

        Integer num7 = new Integer(1);
        Integer num8 = new Integer(1);

        System.out.println(num7 == num8);
    }
}

运行结果:

true
true
false
false

总所周知,对于基本类型,“==”是比较其值,所以 num1 == num1 为 true。对于引用类型,“==”是比较他们的地址值。这里我们有必要了解一下 Java 的包装类和自动装箱机制。

在 Java 中,所有的基本类型都有一个与之对应的子类,这些类称为包装器(wrapper)。这些对象包装器类有很明显的名字:Byte、Short、Integer、Long、Float、Double、Character、Boolean 和 Void。其中,前6个类派生于公共的超类 Number。对象包装器的类是不可变的,即一旦构造了包装器,就不允许修改其中的值。同时,对象包装器还是 final,因此不能定义它们的子类。

自动装箱的规范要求,只有 true 和 false 的 boolean,所有的 byte 值,介于 -128 ~ 127 之间的 short、int 和 long,以及介于 \u0000 ~ \u007F之间的 char 会被包装到固定的对象中。特定的基本类型一定会被包装成相同的包装类型。这些对象被高速缓存以及重复使用。

比如在 Integer 中有这样一个内部类:

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

    static {
        // high value may be configured by property
        int h = 127;
        String integerCacheHighPropValue =
            sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
        if (integerCacheHighPropValue != null) {
            try {
                int i = parseInt(integerCacheHighPropValue);
                i = Math.max(i, 127);
                // Maximum array size is Integer.MAX_VALUE
                h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
            } catch( NumberFormatException nfe) {
                // If the property cannot be parsed into an int, ignore it.
            }
        }
        high = h;

        cache = new Integer[(high - low) + 1];
        int j = low;
        for(int k = 0; k < cache.length; k++)
            cache[k] = new Integer(j++);

        // range [-128, 127] must be interned (JLS7 5.1.7)
        assert IntegerCache.high >= 127;
    }
        
    private IntegerCache() {}
}

通过分析源码,我们可以分析出:cache[] 是一个按顺序存储了 -128 ~ 127 的数组。但是当数字超出这个范围,就会 new 一个 Integer 对象。

补充一点,装箱和拆箱是编译器认可的,而不是虚拟机。编译器在生成字节码时,插入必要的方法调用。虚拟机只是执行这些字节码。

最后修改于 2019-03-21 10:19:23
上一篇