Java中自動裝箱與拆箱


一、什么是封裝類?

  Java中存在基礎數據類型,但是在某些情況下,我們要對基礎數據類型進行對象的操作,例如,集合中只能存在對象,而不能存在基礎數據類型,於是便出現了包裝器類。包裝器類型就是對基本數據類型進行了封裝,使之成為一個對象,每一個基本數據類型都對應一種包裝器類型。

二、什么是裝箱與拆箱

  將基本數據類型變為包裝器類,便是裝箱,將包裝器類轉為基本數據類型就是拆箱。相面以Integer為例:

public static void main(String[] args){
        //自動裝箱
        Integer a = 100;
        //自動拆箱
        int b = a;
    }

實際上,Integer在裝箱過程中調用了Integer中的valueOf()方法,拆箱為int時調用了Integer中的intValue()方法,源碼如下:

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);
    }
public int intValue() {
        return value;
    }

這便是裝箱與拆箱的底層執行過程,其它包裝器類類似。

三、自動裝箱與拆箱中的一些問題

問題1:

public class Main {
    public static void main(String[] args) {

        Integer i1 = 100;
        Integer i2 = 100;
        Integer i3 = 200;
        Integer i4 = 200;

        System.out.println(i1==i2);  //true
        System.out.println(i3==i4);  //false
    }
}

 

通過分析valueOf()方法我們知道,當值為-128到127之間時,返回return IntegerCache.cache[i + (-IntegerCache.low)];當不在這個區間時,返回return new Integer(i);那么IntegerCache.cache[i + (-IntegerCache.low)]又是什么呢?繼續追蹤源碼:

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() {}
    }

從上面代碼可以看出Integer類中提前就初始化了一個Integer數組,范圍為-128到127。回到上面的題目:i1==i2=100返回true是因為范圍屬於-128到127,直接返回cache[]數組中的對象,屬於同一個對象,==運算符比較對象,從而返回true,而200超過了這個范圍,直接返回new Integer(),因此屬於兩個不同的對象,結果為false。

問題2:

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
    }
}

上面的代碼中的結果與Integer類型的結果大相徑庭,原因還得從源碼中找。

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

因此返回false理所當然。

總結一下:

Integer派別:Integer、Short、Byte、Character、Long這幾個類的valueOf方法的實現是類似的。 
Double派別:Double、Float的valueOf方法的實現是類似的。每次都返回不同的對象。

Integer派別的范圍:

問題3:

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
    }
}

以上代碼又是林外一種情況,繼續追蹤源碼

public static Boolean valueOf(boolean b) {
        return (b ? TRUE : FALSE);
    }
/**
     * The {@code Boolean} object corresponding to the primitive
     * value {@code true}.
     */
    public static final Boolean TRUE = new Boolean(true);

    /**
     * The {@code Boolean} object corresponding to the primitive
     * value {@code false}.
     */
    public static final Boolean FALSE = new Boolean(false);

分析可知,TRUE和FALSE是兩個final對象,因此i1、i2和i3、i4始終指向同一個對象的地址,因此返回true。

 問題4:

public static void main(String[] args) {

            Integer num1 = 400;
            int num2 = 400;
            
            System.out.println(num1 == num2); //true
            System.out.println(num1.equals(num2));  //true
        }

可見num1 == num2進行了拆箱操作。equals源碼如下:

public boolean equals(Object obj) {
if (obj instanceof Integer) {
return value == ((Integer)obj).intValue();
}
return false;
}

我們知道equal比較的是內容本身,並且我們也可以看到equal的參數是一個Object對象,我們傳入的是一個int類型,所以首先會進行裝箱,然后比較,之所以返回true,是由於它比較的是對象里面的value值。

問題5:

public static void main(String[] args) {

            Integer num1 = 100;
            int num2 = 100;
            Long num3 = 200l;
            System.out.println(num1 + num2);  //200
            System.out.println(num3 == (num1 + num2));  //true
            System.out.println(num3.equals(num1 + num2));  //false
        }

當一個基礎數據類型與封裝類進行==、+、-、*、/運算時,會將封裝類進行拆箱,對基礎數據類型進行運算。 
對於num3.equals(num1 + num2)為false的原因很簡單,我們還是根據代碼實現來說明:

public boolean equals(Object obj) {
        if (obj instanceof Long) {
            return value == ((Long)obj).longValue();
        }
        return false;
    }

它必須滿足兩個條件才為true: 
1、類型相同 
2、內容相同 
上面返回false的原因就是類型不同。

問題6:

public static void main(String[] args) {

            int num1 = 100;
            int num2 = 200;
            long mum3 = 300;
            System.out.println(mum3 == (num1 + num2)); //true
        }

當 “==”運算符的兩個操作數都是包裝器類型的引用,則是比較指向的是否是同一個對象,而如果其中有一個操作數是表達式(即包含算術運算)則比較的是數值(即會觸發自動拆箱的過程)。

問題7:

public static void main(String[] args) {

            Integer integer100=null;
            int int100=integer100;
        }

這兩行代碼是完全合法的,完全能夠通過編譯的,但是在運行時,就會拋出空指針異常。其中,integer100為Integer類型的對象,它當然可以指向null。但在第二行時,就會對integer100進行拆箱,也就是對一個null對象執行intValue()方法,當然會拋出空指針異常。所以,有拆箱操作時一定要特別注意封裝類對象是否為null。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM