Integer的自動裝箱與拆箱

裝箱和拆箱

  • 裝箱:將基本數據類型轉換成封裝類型。
  • 拆箱:將封裝類型轉換成基本數據類型。

自動裝箱和自動拆箱

在jdk1.5開始增加了自動裝箱和自動拆箱機制,就是爲了方便基本類型和封裝類型之間的互相轉換。
下面來看看自動裝箱(拆箱)和顯示裝箱(拆箱)的例子:

Integer a1 = 3;  // 自動裝箱
Integer a2 = Integer.valueOf(3);  // 顯示裝箱

int a3 = new Integer(3);  // 自動拆箱
int a4 = new Integer(3).intValue();  // 顯示拆箱

自動裝箱 / 拆箱的實現

其實自動裝箱或拆箱是通過編譯器自動執行的,當然調用的方法還是一樣的。下面來看看源碼實現。以Integer爲例:

Integer.valueOf(int i)

首先進入valueOf方法。這裏先判斷傳入的值是否在IntegerCache.low和IntegerCache.high範圍之內,如果在則從IntegerCache.cache數組中直接返回一個對象,否則就new一個新的對象。

public static Integer valueOf(int i) {
    // -128 < i < 127,返回一個緩存對象
    if (i >= IntegerCache.low && i <= IntegerCache.high)
        return IntegerCache.cache[i + (-IntegerCache.low)];
    // 否則新建一個對象
    return new Integer(i);
}

這裏的IntegerCache是個私有內部靜態類,具體看代碼中的註釋。這裏可以清楚的知道上面說的緩存的範圍是在-128~127之間,但是最大值可以通過虛擬機參數修改。

    private static class IntegerCache {
        static final int low = -128; // low默認爲-128
        static final int high; // high的初始化在靜態代碼塊中
        static final Integer cache[]; // 存放緩存對象的數組

        static {
            // 最高值可能由虛擬機參數設置
            int h = 127;
            // 這裏可以通過VM參數-XX:AutoBoxCacheMax來設置high的值
            String integerCacheHighPropValue = sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
            // 判斷是否設置了參數
            if (integerCacheHighPropValue != null) {
                try {
                    int i = parseInt(integerCacheHighPropValue);
                    // high不能小於127
                    i = Math.max(i, 127);
                    // high的最大值不能超過Integer.MAX_VALUE
                    h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
                } catch( NumberFormatException nfe) {
                    // 如果參數不能解析爲int值,則直接忽視它
                }
            }
            // 在這裏設置high的值,如果沒有設置參數,那麼它將是默認的127
            high = h;
            // 初始化cache數組,將容量設置爲high - low,也就是剛好裝下-128~127的Integer對象
            cache = new Integer[(high - low) + 1];
            int j = low;
            // 將-128~127的Integer對象都初始化,然後放在cache中
            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() {}
    }

Integer.intValue()

這個就沒啥好說的啦,就是直接返回這個數值。

public int intValue() {
    return value;
}

下面來看一下可能出現的面試 / 筆試題:

        // Integer類型的實例
		Integer a1 = new Integer(127);
		Integer a2 = new Integer(127);
		Integer a3 = new Integer(128);
		// 基本數據類型
		int a4 = 128;
		// 自動裝箱得到Integer類型的實例
		Integer a5 = a3.intValue();
		Integer a6 = 127;
		Integer a7 = 127;
		// 顯示裝箱得到Integer類型的實例
		Integer a8 = Integer.valueOf(128);

        // 這個肯定爲false啦,因爲a1和a2是兩個不同的實例。兩個實例用等號進行比較的話,會比較對象所引用的地址是否相同,這裏肯定是不同的。
		System.out.println(a1 == a2);
        // 這裏與上面類似,雖然a6會自動裝箱爲Integer類型,但這裏還是兩個不同的實例,所以不相等
		System.out.println(a1 == a6);
        // 因爲a4是個基本類型,封裝類型遇到基本類型會自動拆箱的,a3變爲基本類型。兩個基本類型進行比較直接比較內容是否相等就行啦,這裏兩個數值相等,所以爲true。
		System.out.println(a3 == a4);
        // 這個與上面的類似,自動裝箱類型遇到基本類型又會自動拆箱的。也爲true。
		System.out.println(a4 == a5);
        // 這個就比較有意思了。兩個都是自動裝箱得到的Integer實例,因爲Integer有個緩存機制,如果沒有認真看,應該會認爲是true,其實不然。
        // 因爲這裏的128超出了Integer的默認緩存範圍(-128~127),所以在緩存數組中找不到這個Integer實例,只能重新創新一個新的Integer實例,這就變成了類似第一個的比較,所以爲false
		System.out.println(a5 == a8);
        // 這裏兩個數都是127,在緩存範圍內。所以會直接從緩存數組中取已經提前實例化好的對象,所以取的是同一個對象(引用地址也相同哦),自然結果是true啦。
		System.out.println(a6 == a7);

總結

  • 兩個實例進行比較,不僅要內容相同,還要所引用的對象地址也相同,否則是不相同的(所以比較兩個封裝類型的對象一般使用equals)。
  • 封裝類型遇到基本類型會自動拆箱,所以Integer實例與基本類型比較,只要內容相等,就是相等的。
  • Integer內有個緩存機制,爲了對象的重用。範圍在-128~127的整型通過自動裝箱得到的對象是相同的(返回的是同一個對象),超出範圍則不會相同(會new一個新的對象)。範圍最大值可通過虛擬機參數-XX:AutoBoxCacheMax來設置。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章