1 什麼是自動裝箱?什麼是自動拆箱?
自動裝箱和自動拆箱的示例:
public class Test {
public static void main(String[] args) {
ArrayList<Integer> intList = new ArrayList<Integer>();
intList.add(100);
intList.add(150);
intList.add(200);
int a = intList.get(0);
int b = intList.get(1);
int c = intList.get(2);
System.out.println(a + ", " + b + ", " + c);
}
}
intList清單中隻能放Integer類型的對象,而代碼中把int基本資料類型放到intList清單中,那麼實際上編譯器會把int基本資料類型轉換城Integer類型(包裝類)的對象,那麼編譯器的這個操作過程就是裝箱的過程;而從intList清單中拿取的也是Integer類型的對象,而代碼中把Integer類型的對象指派給int基本資料類型,那麼編譯器會把Integer類型的對象轉換成int基本資料類型,那麼編譯器的這個操作過程就是拆箱的過程;另外,由于裝箱和拆箱都是編譯器自動完成,不需要在代碼中顯式的說明,故稱為自動裝箱和自動拆箱。基本資料類型byte,short,char,int,long,float,double和boolean對應的包裝類為Byte,Short,Character,Integer,Long,Float,Double和Boolean。
2 如何實作自動裝箱和自動拆箱?
自動裝箱和自動拆箱由編譯器自動完成。當編譯器檢測到需要把基本類型轉換成包裝類型時,編譯器自動調用包裝類型的valueOf()方法實作自動裝箱;當編譯器檢測到需要把包裝類型轉換成基本類型時,編譯器自動調用包裝類型的xxValue()方法實作自動拆箱。
八大基本資料類型自動裝箱的原理代碼:
// Byte
public static Byte valueOf(byte b) {
final int offset = 128;
return ByteCache.cache[(int)b + offset];
}
// Short
public static Short valueOf(short s) {
final int offset = 128;
int sAsInt = s;
if (sAsInt >= -128 && sAsInt <= 127) { // must cache
return ShortCache.cache[sAsInt + offset];
}
return new Short(s);
}
// Character
public static Character valueOf(char c) {
if (c <= 127) { // must cache
return CharacterCache.cache[(int)c];
}
return new Character(c);
}
// Ingeger
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
// Long
public static Long valueOf(long l) {
final int offset = 128;
if (l >= -128 && l <= 127) { // will cache
return LongCache.cache[(int)l + offset];
}
return new Long(l);
}
// Double
public static Double valueOf(double d) {
return new Double(d);
}
// Float
public static Float valueOf(float f) {
return new Float(f);
}
// Boolean
public static Boolean valueOf(boolean b) {
return (b ? TRUE : FALSE);
}
從裝箱的源代碼可知,當基本類型為byte,short,char,int和long類型時,首先根據指派的數值大小判斷是否在緩存區,否則直接new一個對應的包裝類型對象;注意隻有double和float會直接new一個包裝類。(因為基本資料類型byte,char和int用的較多,是以Java在包裝類初始化的時候在記憶體中緩存一定範圍的值,避免生成大量的對象。緩存範圍為[-128,127],正好是short基本類型的範圍)。
八大基本資料類型自動拆箱的原理代碼:
public xxx xxxValue() {
return value;
}
3 自動裝箱和自動拆箱有什麼作用?有什麼弊端?
JDK1.5之前,編譯器還沒有實作自動裝箱和自動拆箱,需要顯式的編寫裝箱和拆箱的過程;如下面的代碼:
intList.add(Integer.valueOf(100));
intList.add(Integer.valueOf(150));
intList.add(Integer.valueOf(200));
int _a = intList.get(0).intValue();
int _b = intList.get(1).intValue();
int _c = intList.get(2).intValue();
可以看出代碼很繁雜,JDK1.5之後的valueOf()和intValue()由編譯器自動完成,可以簡化代碼。
自動裝箱和自動拆箱給我們帶來便利的同時,也帶來了弊端,主要展現以下幾點:
- 包裝類之間的比較;
public static void method2() {
Integer a = 100;
int _a = 100;
Integer _a1 = new Integer(100);
System.out.println("a == _a : " + (a == _a)); // true
System.out.println("a == _a1 : " + (a == _a1)); // false
System.out.println("_a == _a1 : " + (_a == _a1)); //true
Integer b = 200;
int _b = 200;
Integer _b1 = new Integer(200);
System.out.println("b == _b : " + (b == _b)); // false
System.out.println("b == _b1 : " + (b == _b1)); // false
System.out.println("_b == _b1 : " + (_b == _b1)); //false
}
==可以用于對象與對象之間的比較,比較的是對象的記憶體位址是否相等,即是否是同一個對象,比較的過程沒有自動拆箱;也可以用于基本資料類型之間的比較,比較的是字面值;是以,對于對象之間的比較應該用equals()方法。
- 稍不留神,容易建立額外對象
Integer result = 0;
for (int i = 127; i < 1127; i++) {
result += i;
}
上面的result += i 實際會先進行自動拆箱操作,然後進行自動裝箱操作。即
result = result.intValue() + i;
result = result.valueOf(result);
自動裝箱實際會建立1000各無用的Integer對象,會給記憶體造成極大的浪費;是以編寫代碼中避免使用不必要的自動裝箱。