裝箱和拆箱
- 裝箱:將基本數據類型轉換成封裝類型。
- 拆箱:將封裝類型轉換成基本數據類型。
自動裝箱和自動拆箱
在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來設置。