1. 程式人生 > >Java原始碼分析——包裝類與Void類解析

Java原始碼分析——包裝類與Void類解析

    在Java中,有著8種基本型別,其對應著8種包裝型別,分別為:Integer、Long、Short、Boolean、Float、Double、Byte以及Character類,這8種包裝型別分別封裝了對應型別的常用操作以及一些優化操作,比如包裝類的快取,自動裝箱等。它們之間的關係如下:
在這裡插入圖片描述
    在Number抽象類中定義了一些系列的基礎型別的轉換行為,所以關於數字操作的都繼承自Number抽象類,而Boolean類與Character類則不是,其原始碼如下:

public abstract
class Number implements java.io.Serializable { public abstract int intValue(); public abstract long longValue(); public abstract float floatValue(); public abstract double doubleValue(); public byte byteValue() { return (byte)intValue(); } public short shortValue
() { return (short)intValue(); } private static final long serialVersionUID = -8742448824652078965L; }

    而閱讀這8個包裝類的時候會發現每個類中有這樣一段程式碼:

public static final Class<包裝類> TYPE = (Class<包裝類>) Class.getPrimitiveClass("對應的包裝類的基本型別");

    Class類的getPrimitiveClass方法是一個Native方法,該方法的作用是根據基本資料型別的名字來獲取對應的Class類的物件的,也就是說不僅在Java中引用是有其Class類物件,基本的資料型別也具有

,我們可以用該方法來直接得到該包裝類的型別:

 System.out.println(Integer.TYPE);
 //輸出:int

    為了在包裝類與基本資料型別之間方便相互轉換,Java實現了一個"語法糖",包裝類的自動裝箱與拆箱。什麼是自動裝箱,比如有下面一段程式碼:

 Byte a=12;
 Integer c=12;

    當把基本的資料型別直接賦值給對應的包裝類的時候就會發生自動裝箱機制,將對應的基本型別轉化為對應的包裝類物件。其中的原理是呼叫了每個包裝類對應的的valueOf方法,也就是說上面的程式碼可以寫成如下的形式:

Byte b=Byte.valueOf((byte) 12);
Integer c=Integer.valueOf(12);
int d=new Integer(10); 3

    而解除安裝則是剛好反了過來,如3式。以Integer為例,展示它的valueOf原始碼:

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

    當然,你會發現其中的IntegerCache類,這個類是用來幹嘛的呢?是用來利用快取來優化的,除了Boolean類、Float類、Double類外,其它的五個包裝類都實現了快取,其中Integer、Long、Short、Byte類實現了一個位元組數的快取,即-128~127之間是直接從快取中獲取,而不用直接建立的,而Character類是快取了0到255的字元。以Byte類的快取為例,原始碼如下:

private static class ByteCache {
        private ByteCache(){}
        static final Byte cache[] = new Byte[-(-128) + 127 + 1];
        static {
            for(int i = 0; i < cache.length; i++)
                cache[i] = new Byte((byte)(i - 128));
        }
    }

    從程式碼中可以看出,快取類是一個靜態類,也就是說快取實在類載入的時候就已經完成了,當呼叫valueOf方法建立包裝類的時候,如果在其快取的範圍內,則直接從快取中取出,否則直接建立一個新的包裝類,這樣就實現了局部的優化,知道了這個快取機制的話,就可以在如下的程式碼中輕鬆的判斷出結果:

Byte a=1;
Byte b=1;
Byte c=new Byte((byte)1);
System.out.println(a==b);//true
System.out.println(a==c);//false

    另外有趣的是,Float類與Double類定義了NaN,也就是表示無窮,表示一個不確定的數,這個含義是兩個NaN做相等比較永遠是false的:

//正無窮大
public static final double POSITIVE_INFINITY = 1.0 / 0.0;
//負無窮大
public static final double NEGATIVE_INFINITY = -1.0 / 0.0;
//無窮大
public static final double NaN = 0.0d / 0.0;
System.out.println((0.0d / 0.0)==(0.0d / 0.0));//false

    關於Void類的原始碼如下:

public final
class Void {
    /**
     * The {@code Class} object representing the pseudo-type corresponding to
     * the keyword {@code void}.
     */
    @SuppressWarnings("unchecked")
    public static final Class<Void> TYPE = (Class<Void>) Class.getPrimitiveClass("void");
 
    /*
     * The Void class cannot be instantiated.
     */
    private Void() {}
}

    官方對它的解釋是Void類是一個不可例項化的佔位符類,它持有對標識Java關鍵字void的Class物件的引用。它用於模擬void型別,是不可例項化的,它的值只有null值,至於好處貌似就是它的用途。