詳解 Java 的自動裝箱與拆箱

1、什麼是自動裝箱?自動拆箱?

  • 從 Java SE5 開始就提供了自動裝箱的特性,簡單一點說:
    裝箱就是 自動將基本數據類型轉換爲包裝器類型;
    拆箱就是 自動將包裝器類型轉換爲基本數據類型。java

    //自動裝箱
    Integer i = 10;  
    
    //自動拆箱
    int n = i;   
    複製代碼
  • 下表是基本數據類型對應的包裝器類型:面試

基本數據類型 包裝器類型
byte(1字節) Byte
short(2字節) Short
int(4字節) Integer
long(8字節) Long
float(4字節) Float
double(8字節) Double
char(2字節) Character
boolean(4字節) Boolean

2、裝箱和拆箱如何實現

以 Integer 類爲例,下面看一段代碼:數組

public class Test {
	public static void main(String[] args) {
		Integer i = 10;
		int j = i;
	}
}
複製代碼

先編譯:javac Test.java
再反編譯:javap -c Test.class
字節碼輸出以下: 緩存

能夠看出:

在裝箱的時候自動調用 Integer 的 valueOf (int) 方法;  
拆箱的時候自動調用 Integer 的 intValue 方法。  
所以能夠用一句話總結裝箱和拆箱的實現過程:  
裝箱過程是經過調用包裝器的 valueOf 方法實現;  
而拆箱過程是經過調用包裝器的 xxxValue 方法(xxx 表明對應的基本數據類型)。
複製代碼

3、面試中的相關問題

下面列舉一些常見的與裝箱 / 拆箱有關的面試題。bash

1. Integer

public class Main {
    public static void main(String[] args) {
        Integer i1 = 100;
        Integer i2 = 100;
        Integer i3 = 128;
        Integer i4 = 128;
         
        System.out.println(i1==i2); // true
        System.out.println(i3==i4); // false
    }
}
複製代碼

輸出結果代表 i1 和 i2 指向的是同一個對象,而 i3 和 i4 指向的是不一樣的對象。
源碼分析一下 Integer 的 valueOf 方法的具體實現:源碼分析

public static Integer valueOf(int i) {
        assert IntegerCache.high >= 127;
        if (i >= IntegerCache.low && i <= IntegerCache.high)
            return IntegerCache.cache[i + (-IntegerCache.low)];
        return new Integer(i);
}
複製代碼

其中,IntegerCache 的源碼實現以下:ui

private static class IntegerCache {
        static final int low = -128;
        static final int high;
        static final Integer cache[];

        static {
            // high value may be configured by property
            int h = 127;
            String integerCacheHighPropValue =
                sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
            if (integerCacheHighPropValue != null) {
                int i = parseInt(integerCacheHighPropValue);
                i = Math.max(i, 127);
                // Maximum array size is Integer.MAX_VALUE
                h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
            }
            high = h;

            cache = new Integer[(high - low) + 1];
            int j = low;
            for(int k = 0; k < cache.length; k++)
                cache[k] = new Integer(j++);
        }

        private IntegerCache() {}
}
複製代碼

從這 2 段代碼能夠看出:spa

  • 在經過 valueOf 方法建立 Integer 對象的時候,若是數值在 [-128,127] 之間,便返回指向靜態緩存 IntegerCache.cache 的數組中已經存在的對象的引用;不然建立一個新的 Integer 對象。3d

  • 上面的代碼中 i1 和 i2 的數值爲 100,所以會直接從 cache 中取已經存在的對象,因此 i1 和 i2 指向的是同一個對象,而 i3 和 i4 則是分別指向不一樣的對象。code

2. Double

public class Main {
    public static void main(String[] args) {
        Double i1 = 100.0;
        Double i2 = 100.0;
        Double i3 = 200.0;
        Double i4 = 200.0;
         
        System.out.println(i1==i2); // false
        System.out.println(i3==i4); // false
    }
}
複製代碼

其中 Double 的 valueOf 源碼以下:

public static Double valueOf(double d) {
        return new Double(d);
}
複製代碼

注意:
Integer、Short、Byte、Character、Long 這幾個類的 valueOf 方法的實現是相似的。
Double、Float 的 valueOf 方法的實現是相似的。

3. Boolean

public class Main {
    public static void main(String[] args) {
        Boolean i1 = false;
        Boolean i2 = false;
        Boolean i3 = true;
        Boolean i4 = true;
         
        System.out.println(i1==i2); // true
        System.out.println(i3==i4); // true
    }
}
複製代碼

Boolean 類的源碼的 valueOf 實現:

public static Boolean valueOf(boolean b) {
        return (b ? TRUE : FALSE);
}
複製代碼
public static final Boolean TRUE = new Boolean(true);
public static final Boolean FALSE = new Boolean(false);
複製代碼

從這2段代碼能夠看到:

  • 返回的都是 true,也就是它們執行 valueOf 返回的都是相同的對象。
  • 它並無建立對象,而是返回在內部已經提早建立好兩個靜態全局對象,由於它只有兩種狀況,這樣也是爲了不重複建立太多的對象。

4. == 和 equals

public class Main {
    public static void main(String[] args) {
        Integer a = 1;
        Integer b = 2;
        Integer c = 3;
        Integer d = 3;
        Integer e = 321;
        Integer f = 321;
        Long g = 3L;
        Long h = 2L;
         
        System.out.println(c==d); // true
        System.out.println(e==f); // false
        System.out.println(c==(a+b)); // true 拆箱 比較值
        System.out.println(c.equals(a+b)); // true 先拆箱再裝箱,比較對象裏面的value值
        System.out.println(g==(a+b)); // true
        System.out.println(g.equals(a+b)); // false 類型不一樣
        System.out.println(g.equals(a+h)); // true 拆箱、向上轉型、裝箱、比較value值
    }
}

複製代碼

注意:

  • 當一個基礎數據類型與封裝類進行 ==、+、-、*、/ 運算時,會將封裝類進行拆箱,對基礎數據類型進行運算;
  • 當 "==" 運算符的兩個操做數都是 包裝器類型的引用,則是比較指向的是不是同一個對象;
  • 而若是其中有一個操做數是表達式(即包含算術運算)則比較的是數值(即會觸發自動拆箱的過程);
  • equals 它必須知足兩個條件才爲 true:
    1) 類型相同
    2) 內容相同

5. 談談 Integer i = new Integer (xxx) 和 Integer i =xxx; 這兩種方式的區別。

主要有如下這兩點區別: 1) 第一種方式不會觸發自動裝箱的過程;而第二種方式會觸發; 2) 在執行效率和資源佔用上的區別。第二種方式的執行效率和資源佔用在通常性狀況下要優於第一種狀況(注意這並非絕對的)。

相關文章
相關標籤/搜索