自动装箱和拆箱的几个细节

装箱和拆箱

装箱和拆箱也比较简单,我就不解释了,直接看代码就行了。

Integer box = 2; // 自动装箱
System.out.println(box); // 自动拆箱

虽然装箱拆箱简单易理解,但是其实 JDK 源码中有一些小细节,如果平时没注意,可能一不小心就踩了个坑。

细节一

public class Boxing {
    public static void main(String[] args) {
        System.out.println("Integer.valueOf(-128) == Integer.valueOf(-128) ? " + (Integer.valueOf(-128) == Integer.valueOf(-128)));
        System.out.println("Integer.valueOf(-129) == Integer.valueOf(-129) ? " + (Integer.valueOf(-129) == Integer.valueOf(-129)));
        System.out.println("Integer.valueOf(128) == Integer.valueOf(128) ? " + (Integer.valueOf(128) == Integer.valueOf(128)));
        System.out.println("Integer.valueOf(127) == Integer.valueOf(127) ? " + (Integer.valueOf(127) == Integer.valueOf(127)));
    }
}
输出:
Integer.valueOf(-128) == Integer.valueOf(-128) ? true
Integer.valueOf(-129) == Integer.valueOf(-129) ? false
Integer.valueOf(128) == Integer.valueOf(128) ? false
Integer.valueOf(127) == Integer.valueOf(127) ? true

这个输出就比较奇怪了,让我们直接看一下源码,看看 Integer.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 做了一个数组,缓存了 -128 到 127 之间的 Integer,在这个范围内的 Integer,会直接返回数组中对应索引的 Integer,而超出这个范围的,则是直接 new Integer(i)。
其实这么做也是挺合理的,-128到127是比较常用的范围,直接缓存起来,也不用每次都去生成实例,减少了开销。

细节二

public class Boxing {
    public static void main(String[] args) {
        Integer box1 = 2;
        Integer box2 = box1;
        System.out.println(box2 == box1);
        box1++;
        System.out.println(box2 == box1);
    }
}
输出:
true
false

为什么我们对 box1 做了个自增之后,就引用不一致了呢?
这里是因为 box1++ 实际上进行了拆箱又装箱。

// box1++
int box1 = box1.intValue();
box1++;
Integer box1 = new Integer(box1);

经过这个拆箱又装箱,引用肯定就跟原来的不一样了。

细节三

    System.out.println("new Integer(2) == new Integer(2) ? " + (new Integer(2) == new Integer(2)));
        System.out.println("new Integer(2).equals(new Integer(2)) ? " + new Integer(2).equals(new Integer(2)));
输出:
new Integer(2) == new Integer(2) ? false
new Integer(2).equals(new Integer(2)) ? true

== 为 false,而 equals 为 true,这个我们很自然应该会想到, equals 是不是被重写了呢?让我们直接看源码。

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

这下就很直观了,JDK 对传进来的参数 obj 做了一个拆箱,直接判断值,而不是引用,所以应该返回 true。

写在最后

虽然拆箱装箱是一个很简单的过程,但是这几个细节,平时遇到了,如果我们不去看源码,可能有时候会一头雾水,一看源码,就豁然开朗。所以其实日常开发中,也应该有一个踩到坑就看看源码怎么实现的习惯的。

猜你喜欢

转载自www.cnblogs.com/wengzp/p/9342984.html