天天看點

【JAVA】談談拆箱與裝箱

                                          談談裝箱與拆箱

一、何為包裝類型

Java是一種面向對象的語言,但是它不是純面向對象的。Java中存在基本資料類型,談不上對象。為了向純面向對象靠攏,Java5的時候推出了基本資料類型的包裝類型。

基本資料類型與包裝類型的對應關系如下: 

【JAVA】談談拆箱與裝箱

二、何為裝箱與拆箱

裝箱就是将基本資料類型轉化為包裝類型,那麼拆箱就是将包裝類型轉化為基本資料類型。

以基本資料類型int為例:

package day1119;

public class TestBox {
    public static void main(String[] args) {
        //自動裝箱,底層其實執行了Integer a=Integer.valueOf(1);
        Integer a = 1;
        //自動拆箱,底層其實執行了int b=a.intValue();
        int b = a;
    }
}      

Integer的valueOf(int i)方法可以将一個基本資料類型轉化為對應的包裝類型,即裝箱方法。

而Integer的intValue()方法則可以将一個包裝類型轉化為對應的基本資料類型,即拆箱方法。

三、裝箱與自動裝箱的差別

裝箱:利用Integer的構造方法Integer(int value),即Integer c=new Integer(1);

自動裝箱:或者叫隐式裝箱,直接給Integer指派,即Integer d=1,在編譯的時候,會調用Integer.valueOf()方法完成裝箱。

相比而言,自動裝箱可能比裝箱具有更高的效率,展現在自動裝箱的緩存上,下面從幾道題目來講自動裝箱的緩存。

四、相關面試題目

第一題:以下代碼的輸出結果為?(==号兩邊如果都是引用類型的話,則判斷它們是否指向同一個對象。如果都是基本資料類型的話,則判斷它們的數值是否相等)

package day1119;

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

也許有些人認為他們是四個各不相同的對象,兩個式子都傳回false。

實際運作後發現輸出:

【JAVA】談談拆箱與裝箱

為什麼一個是true,一個是false呢?

剛才我們知道,Integer a=100這條語句會觸發自動裝箱,而自動裝箱的方法為Integer.valueOf()方法,讓我們去尋找這個方法,一探究竟。

觀察Integer類的源碼中的valueOf()

/**
     * Returns an {@code Integer} instance representing the specified
     * {@code int} value.  If a new {@code Integer} instance is not
     * required, this method should generally be used in preference to
     * the constructor {@link #Integer(int)}, as this method is likely
     * to yield significantly better space and time performance by
     * caching frequently requested values.
     *
     * This method will always cache values in the range -128 to 127,
     * inclusive, and may cache other values outside of this range.
     *
     * @param  i an {@code int} value.
     * @return an {@code Integer} instance representing {@code i}.
     * @since  1.5
     */
    public static Integer valueOf(int i) {
        if (i >= IntegerCache.low && i <= IntegerCache.high)
            return IntegerCache.cache[i + (-IntegerCache.low)];
        return new Integer(i);
    }      

可以看得出,當i的值位于[-128,127]的時候,會直接傳回Integer緩存數組中相應對象的引用,如果i大于127或小于-128,會重新建立一個Integer執行個體,并傳回。

那麼第一條式子a和b的值都在緩存範圍内,是以他們指向同一個對象,是以傳回true。c和d的值不在範圍内,都是通過new建立出來的,是以不是同一個對象,傳回false。

注意:Byte、Short、Integer、Long、Character的valueOf()實作機制類似。

其中相同值的Byte比較永遠傳回true,因為byte取值範圍就是[-128,127]。

Short、Integer、Long的valueOf()基本一樣,i的範圍都需要在[-128,127]。

Character中i的範圍隻要小于等于127即可,因為char最小值為0,本來就大于等于-128。

但是Float、Double中的valueOf(),永遠傳回新建立的對象,因為一個範圍内的整數是有限的,但是小數卻是無限的,無法儲存在緩存中。

package day1119;

public class TestBox3 {
    public static void main(String[] args) {
        Integer a = 1;
        Integer b = 2;
        Integer c = 3;
        Long d = 2L;
        Long e = 3L;
        int f = 2;

        //一旦有包裝類型和數值類型判斷==時,則觸發包裝類型的自動拆箱,轉為數值類型的比較
        System.out.println(new Integer(300) == 300);//傳回true

        //一旦有包裝類型和數值類型發生運算時,則觸發包裝類型的自動拆箱,轉為數值類型的運算
        System.out.println(c == (a + f));//傳回true

        //一旦有包裝類型和包裝類型發生運算時,則觸發包裝類型的自動拆箱,轉為數值類型的運算
        System.out.println(c == (a + b));//傳回true

        //隻有對象類型才有equals方法,是以首先a,b觸發包裝類型的自動拆箱,轉為數值類型的運算。
        //運算完,再将結果3自動裝箱,Integer重寫了equals,是以可以轉為包裝類型與包裝類型的比較。
        //當兩邊的包裝類型不一緻時,必定傳回false。
        //當兩邊的包裝類型一緻時,再進行拆箱,判斷兩者代表的數值是否相等。
        System.out.println(c.equals(a + b));//傳回true

        //不同資料類型的數值進行運算,首先會将低精度的資料類型轉化為高精度的資料類型,即自動類型轉換。
        //比如現在的int+long,會提升到long+long,再進行運算。
        System.out.println(e == (a + d));//傳回true

        //==号兩邊類型不一緻時,直接執行自動拆箱,比較之後的數值
        System.out.println(e == (a + b));//傳回true

        //依次經曆自動拆箱,自動類型轉換、運算、自動裝箱,類型比較,拆箱,數值比較
        System.out.println(e.equals(a + d));//傳回true

        //依次經曆自動拆箱,自動類型轉換、運算、自動裝箱,類型比較,兩邊類型不一緻,直接傳回false
        System.out.println(c.equals(a + d));//傳回false


    }
}      

五、總結

繼續閱讀