【JAVA】談談拆箱與裝箱
談談裝箱與拆箱
一、何為包裝型別
Java是一種面向物件的語言,但是它不是純面向物件的。Java中存在基本資料型別,談不上物件。為了向純面向物件靠攏,Java5的時候推出了基本資料型別的包裝型別。
基本資料型別與包裝型別的對應關係如下:
二、何為裝箱與拆箱
裝箱就是將基本資料型別轉化為包裝型別,那麼拆箱就是將包裝型別轉化為基本資料型別。
以基本資料型別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。
實際執行後發現輸出:
為什麼一個是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(),永遠返回新建立的物件,因為一個範圍內的整數是有限的,但是小數卻是無限的,無法儲存在快取中。
Boolean中只有兩個物件,要麼是true的Boolean,要麼是false的Boolean,只要boolean的值相同,Boolean就相等。
第二題:以下程式碼的輸出結果為?
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
}
}
五、總結
如果想要深入瞭解自動裝箱拆箱的過程,必須得反編譯class檔案,瞭解底層編譯的細節,才可以解除自己此方面的疑問。