關(guān)于 Integer 和裝箱、拆箱

一、關(guān)于拆箱和裝箱

裝箱 就是自動將基本數(shù)據(jù)類型轉(zhuǎn)換為包裝器類型;拆箱 就是自動將包裝器類型轉(zhuǎn)換為基本數(shù)據(jù)類型

基本數(shù)據(jù)類型 包裝器類型
int(4子節(jié)) Integer
byte(1子節(jié)) Byte
short(2子節(jié)) Short
long(8子節(jié)) Long
float(4子節(jié)) Float
double(8子節(jié)) Double
char(2子節(jié)) Character
boolean(未定) Boolean

具體是怎么進行裝箱的呢?

如果需要生成一個數(shù)值的對象,那么只需要調(diào)用

Integer a = 100;    //裝箱

這個過程就會根據(jù)數(shù)值創(chuàng)建對應(yīng)的 Integer 對象這就是裝箱

而拆箱呢,直接使用

int b = a;      //拆箱

這個時候就是拆箱

我們用一個例子來反編譯一下,看看整個過程調(diào)用了那些方法

public class IntegerTest {
    public static void main(String[] args) {
        Integer a = 100;
        int b = a;
    }
}

反編譯之后的結(jié)果如下:

從反編譯的結(jié)果可以看到,在裝箱的時候,調(diào)用了 Integer 的 valueOf(int i) 方法;而在拆箱的時候,則調(diào)用了 Integer 的 intValue() 方法

我試了一下其他的一些基本類型

public class IntegerTest {
    public static void main(String[] args) {
        Double d = 200d;
        double f = d;

        Short aa = 1;
        short bb = aa;

        Long l = 10000000l;
        long ll = l;
    }
}

經(jīng)過反編譯

果不其然,都調(diào)用了各自包裝類型的 valueOf(xxx) 方法和 xxxValue() 方法

因此可以總結(jié)一下:

裝箱過程就是通過包裝器的 valueOf() 方法實現(xiàn)的,而拆箱過程是通過調(diào)用包裝器的 xxxValue() 方法實現(xiàn)的

二、關(guān)于 Integer.valueOf

由于 Integer 的這個方法蘊含著一些密碼,因此我們拿源代碼來分析一下

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

IntegerCache 的源碼如下

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) {
            try {
                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);
            } catch( NumberFormatException nfe) {
                // If the property cannot be parsed into an int, ignore it.
            }
        }
        high = h;

        cache = new Integer[(high - low) + 1];
        int j = low;
        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() {}
}

通過源碼可以看到,在 Interger 的 valueOf 方法中,如果數(shù)值在 [-127, 128] 范圍的時候,就會去 IntegerCache.cache 這個數(shù)組尋找有沒有存在的 Integer 對象的引用,如果有,則直接返回對象的引用,如果沒有(超過了范圍),就新建一個 Integer 對象

這個是 cache 數(shù)組中已經(jīng)存在的對象,只是一部分,數(shù)組下標范圍為 [0,255],對應(yīng)數(shù)值范圍是 [-128,127]

三、相關(guān)面試題

1.輸出以下結(jié)果

Integer a = 100;
Integer b = 100;
Integer a1 = 200;
Integer b1 = 200;
Integer a2 = new Integer(100);

a == b;
a1 == b1;
a == a2;

結(jié)果: true、false、false

a 和 b 的數(shù)值為100的時候,因為 cache 數(shù)組中已經(jīng)存在對象,因此可以直接取出;

a 和 b 的數(shù)值為200時,因為 cache 數(shù)組中沒有相應(yīng)的對象,因此只能 new 一個 Integer 對象,此時 a1 和 b1 分別指向不同的對象;

a 和 a2 比較的時候,由于 a 調(diào)用 valueOf() 進行自動裝箱,而 a2 已經(jīng)是 Integer 對象,相當(dāng)于兩個不同對象之間的比較,因為地址不同,所以肯定不等

需要注意的是,這整個過程中,都沒有用到拆箱的過程,可以通過反編譯進行驗證


2.輸出以下代碼運行的結(jié)果

Double i1 = 100.0;
Double i2 = 100.0;
Double i3 = 200.0;
Double i4 = 200.0;

i1 == i2
i3 == i4

結(jié)果:false、false。

為什么呢?由于 Double 在進行裝箱的時候也會調(diào)用 valueOf() 這個方法,因此看一下源代碼

public static Double valueOf(double d) {
    return new Double(d);
}

public Double(double value) {
    this.value = value;
}

可以看到,Double 并沒有 Integer 的冠軍機制,而是直接返回了一個新的 Double 對象,因此以上的四個引用都是指向不同的對象的,所以不同

PS

為什么 Double 類的 valueOf() 方法會采用與 Integer 類的 valueOf() 方法不同的實現(xiàn)呢?

很簡單:在某個范圍內(nèi)的整型數(shù)值的個數(shù)是有限的,而浮點數(shù)卻不是。

我又查看了其他幾種數(shù)據(jù)類型的 valueOf 源碼

其中,==Double 和 Float 沒有緩存機制,都是直接返回新的對象== ;==Integer、Short、Byte、Character 有緩存機制==

3.輸出以下代碼運行的結(jié)果

Boolean b1 = false;
Boolean b2 = false;
Boolean b3 = true;
Boolean b4 = true;

b1 == b2
b3 == b4
b1 == b3

結(jié)果:true、true、false

為什么會這樣呢?看一下 Boolean 的 valueOf() 的源代碼

public static Boolean valueOf(boolean b) {
    return (b ? TRUE : FALSE);
}

其中的 TRUE 和 FALSE,代表兩個靜態(tài)成員屬性

public static final Boolean TRUE = new Boolean(true);

public static final Boolean FALSE = new Boolean(false);

因此可以知道了,每次傳入的 true 或 false,都是指向同一個 Boolean 對象,因此他們的引用肯定相同了

4.輸出以下代碼運行的結(jié)果

Integer a = 10;
Integer b = 20;
Integer c = 30;
Integer d = 30;
Integer e = 300;
Integer f = 300;
Integer g = a + b;

c == d;
e == f;
c == g;
c == (a + b);
c.equals(a + b);

結(jié)果:true、false、true、true、true

先注意這句話:當(dāng) "=="運算符的兩個操作數(shù)都是 包裝器類型的引用,則是比較指向的是否是同一個對象,而如果其中有一個操作數(shù)是表達式(即包含算術(shù)運算)則比較的是數(shù)值(即會觸發(fā)自動拆箱的過程)

前兩個沒什么好說的

第三個:由于運用了算術(shù)運算符(+),因此右邊的算式在進行計算之前會先調(diào)用 intVal() 方法進行拆箱,在進行相加,然后得到的結(jié)果30之后,由于前面是 Integer g,因此還會再調(diào)用 valueOf() 方法進行裝箱。由于此時 cache() 數(shù)組中已經(jīng)有了 30 了,因此直接指向緩存池中的 30 這個 Integer 對象。此時 c == g 比較的還是對象的地址是否相同

第四個:由于右邊是 a+b,包含算術(shù)運算,因此會調(diào)用 intVal() 方法,將左邊的 c 進行拆箱,之后又分別對 a 和 b 進行拆箱,即一共調(diào)用了三次拆箱過程,最后比較的是數(shù)值大小,而不是地址

第五個:equals() 方法會先除非自動拆箱,然后在觸發(fā)自動裝箱。a + b 的時候,會先調(diào)用兩次 intVal() 進行拆箱,然后再調(diào)用 valueOf() 方法進行裝箱,此時再用 equals() 方法比較。由于都是 Integer,且數(shù)值相等,則返回 true


5.輸出以下代碼運行的結(jié)果

Integer a = 10;
Integer b = 20;
Long g = 30L;
Long h = 20L;

g == (a + b);
g.equals(a + b);
g.equals(a + h);

結(jié)果:true、false、true

和上面一樣,同樣是使用拆箱,但是需要注意的是,Integer 類型調(diào)用 Integer.valueOf 進行拆箱,而 Long 類型調(diào)用 Long.valueOf 進行拆箱

最后一個結(jié)果是 true,因為 a + h 會使小的精度轉(zhuǎn)為大的精度,最終的 30 是 Long 類型的,因此結(jié)果是 true

四、參考

深入剖析Java中的裝箱和拆箱

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容