天天看點

面試 | 自動裝箱實作原理?類型轉換實作原理?

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對象,會給記憶體造成極大的浪費;是以編寫代碼中避免使用不必要的自動裝箱。