Tip小杂记——java中Integer自动装箱与拆箱之迷

起源

为何会探讨这个标题,起源来自一次笔试题,题目如下:

public static void main(String[] args) {
    Integer a = 100;
    Integer b = 100;
    System.out.println(a == b);
    a = 200;
    b = 200;
    System.out.println(a == 200);
    System.out.println(b == 200);
    System.out.println(a == b);
}

请写出这段代码的输出内容。正确的输出内容为:

true
true
true
false

让人不解的地方在于第一个输出与最后一个输出结果不一致,出现这样结果的原因要从Integer的自动装箱和拆箱来说起。

要将int与Integer互相转换时可以如下写:

Integer i = 10;
int n = i;

第1行代码Integer类会自动将int类型装箱为Integer,第二行代码Integer会自动拆箱为int。装箱和拆箱成为了Java自身做的工作,将方法的调用隐藏了起来,其实装箱和拆箱的过程分别调用了Integer的valueOf()和intValue()方法,首先来看valueOf方法的源码。

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

从源码可以看出,Integer的装箱与IntegerCache存在关系,特别是与IntegerCache.cache格外相关,接着再来看一下IntegerCache的源码。

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() {}
}

可以看出其中的low和high默认分别是-128和127,而cache数组存储了从low至high递增的Integer。而当valueOf方法中,当传入的参数也就是装箱的int在low至high的范围内时,Integer会从cache数组中取出相应的缓存Integer对象返回。接着回到题目代码中

Integer a = 100;
Integer b = 100;
System.out.println(a == b);
a = 200;
b = 200;
System.out.println(a == b);

第3行会输出true,就是因为100在low与high之间,a和b都是从cache数组中取出的同一个Integer对象,所以第3行代码输出为true。但第4、5行代码的200不在low至high之间,此时a和b都是新new的Integer对象,是两个对象,所以第6行代码输出为false。

装箱讲了,接下来讲一下拆箱,先看拆箱的方法intValue源码:

public int intValue() {
    return value;
}

返回的value,这个value就是装箱时的int值,再回到题目:

Integer a = 200;
Integer b = 200;
System.out.println(a == 200);
System.out.println(b == 200);

当第3、4行代码中a、b的Integer对象与int值比较,Integer已经自动拆箱,而a、b的Integer对象拆箱后就是int值200,所以第4、5行都输出true。

最后再来看一下Integer的equals方法源码:

public boolean equals(Object obj) {
    if (obj instanceof Integer) {
        return value == ((Integer)obj).intValue();
    }
    return false;
}

equals方法是调用Integer的intValue方法与传入对象比较,也就是用int值与传入对象比较。

总结

Integer的装箱会根据要装箱的int值大小选择从Integer的缓存中取出Integer对象或者新创建一个Integer对象返回,而Integer的自动拆箱会返回被装箱的int值,因为Integer的equals被重写过,建议Integer对象进行相等比较时使用equals方法。

你可能感兴趣的:(Tip小杂记——java中Integer自动装箱与拆箱之迷)