1. 程式人生 > >自動裝箱和拆箱的幾個細節

自動裝箱和拆箱的幾個細節

是不是 返回 之間 println 實例 eof == 常開 equal

裝箱和拆箱

裝箱和拆箱也比較簡單,我就不解釋了,直接看代碼就行了。

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。

寫在最後

雖然拆箱裝箱是一個很簡單的過程,但是這幾個細節,平時遇到了,如果我們不去看源碼,可能有時候會一頭霧水,一看源碼,就豁然開朗。所以其實日常開發中,也應該有一個踩到坑就看看源碼怎麽實現的習慣的。

自動裝箱和拆箱的幾個細節