啃碎JDK源碼(二):Integer

上次咱們已經對String類源碼作了一個簡單的總結,感興趣的小夥伴能夠去看一下啃碎JDK源碼(一):String,今天來看看Java的Integer包裝類。java

先來看看Integer實現了哪些接口git

public final class Integer extends Number implements Comparable<Integer> {

能夠看到Integerfinal修飾,表明不可被繼承,繼承Number類實現Comparable接口。面試

private final int value;
public static final int MIN_VALUE = 0x80000000;//最大值
public static final int MAX_VALUE = 0x7fffffff;//最小值
public static final Class<Integer>  TYPE = (Class<Integer>) Class.getPrimitiveClass("int");

能夠看出使用了int類型的value來存儲值,而且定義了Integer的最大值爲2^31-1,最小值爲-2^31Integer的基本數據類型爲intsegmentfault

先來看一下Integer的兩個構造函數:數組

public Integer(int value) {
    this.value = value;
}
public Integer(String s) throws NumberFormatException {
    this.value = parseInt(s, 10);
}

那麼這個parseInt方法是幹啥用的呢?緩存

方法parseInt(String s,int radix)的目的是輸出一個十進制數。less

好比:parseInt(1010,2)
意思就是:輸出2進制數1010在十進制下的數。ide

咱們平時用到Integer.parseInt("123");其實默認是調用了int i =Integer.parseInt("123",10);函數

下面是源碼:優化

public static int parseInt(String s, int radix)
                throws NumberFormatException
    {
        /*
         * WARNING: This method may be invoked early during VM initialization
         * before IntegerCache is initialized. Care must be taken to not use
         * the valueOf method.
         */

        if (s == null) {
            throw new NumberFormatException("null");
        }
        //判斷基數是否在 2~36之間
        if (radix < Character.MIN_RADIX) {
            throw new NumberFormatException("radix " + radix +
                                            " less than Character.MIN_RADIX");
        }

        if (radix > Character.MAX_RADIX) {//36
            throw new NumberFormatException("radix " + radix +
                                            " greater than Character.MAX_RADIX");
        }

        int result = 0;
        boolean negative = false;//是否爲負數,默認爲false
        int i = 0, len = s.length();
        int limit = -Integer.MAX_VALUE;
        int multmin;
        int digit;

        if (len > 0) {
            char firstChar = s.charAt(0);
            if (firstChar < '0') { // Possible leading "+" or "-"
                if (firstChar == '-') {//若是是負數,negative賦值爲true,限制變爲int的最小值
                    negative = true;
                    limit = Integer.MIN_VALUE;
                } else if (firstChar != '+')
                    throw NumberFormatException.forInputString(s);

                if (len == 1) // Cannot have lone "+" or "-"
                    throw NumberFormatException.forInputString(s);
                i++;
            }
            /**multmin防止數據溢出 
            *若是是正數就是-2,147,483,64 
            *若是是負數就是-2,147,483,64 **/
            multmin = limit / radix;
            while (i < len) {
                // Accumulating negatively avoids surprises near MAX_VALUE
                //獲取字符轉換成對應進制的整數
                digit = Character.digit(s.charAt(i++),radix);
                if (digit < 0) {
                    throw NumberFormatException.forInputString(s);
                }
                if (result < multmin) {
                    throw NumberFormatException.forInputString(s);
                }
                result *= radix;
                if (result < limit + digit) {
                    throw NumberFormatException.forInputString(s);
                }
                result -= digit;
            }
        } else {
            throw NumberFormatException.forInputString(s);
        }
        return negative ? result : -result;
    }

valueOf

接下來來看一個比較經典的問題,看下面代碼:

Integer a = 100;
Integer b = 100;
Integer c = 200;
Integer d = 200;
System.out.println(a == b);
System.out.println(c == d);

你認爲這裏是四個不一樣的對象,那麼輸出應該都是flase?

不,輸出結果爲:

true
false

經過javap -c/javap -verbose 命令能夠查看字節碼;紅色圈圈裏就是咱們jdk5以後的基本類型的自動包裝的字節碼實現,能夠看出,此處是調用了Integer.valueOf(..)方法的:說白了就是Integer a = 100 等價於Integer a = Integer.valueOf(100)

image.png

JDK1.5以後,java提供了自動裝箱和自動拆箱的功能。自動裝箱也就是調用了Integer類的一個靜態方法valueOf方法,那咱們來看看源碼是如何實現的:

image.png

看看IntegerCache是個什麼東西:

image.png

能夠看到IntegerCache是一個靜態內部類,low的值已經寫死-128,而high的值由你的虛擬機決定sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high"),既然是一個參數也就意味着你能夠動態設置。而後在循環中將low - high之間數字的裝箱後方法cache[]這個Integer類型的數組中。這樣就完成了緩存。

由於當咱們調用valueOf方法時傳入-128到127之間的數字時,Integer會給咱們返回一樣的對象,記住這一點之後面試的時候也能夠和麪試官吹吹牛逼了!

image.png

接下來咱們來看看Integer實現的equalshashcodetoString等方法:

equals 和 hashcode

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

能夠看到它會先判斷類型是否符合,而後進行拆箱比較操做。

看下hashcode方法:

@Override
public int hashCode() {
    return Integer.hashCode(value);
}

public static int hashCode(int value) {
    return value;
}

能夠看到hashcode直接返回自己的int值。

toString

IntegertoString方法 是我認爲一個比較有趣的地方:

image.png

看下toString(int i)源碼:

image.png

在上面用到了stringSize方法,就是求這個Integer數的長度,咱們來看看他是如何實現的:

image.png

能夠看到這段代碼在計算Integer數長度時,構建了一個一維數組,而後拿x與數組每一個值進行比較。

還有一個getChars方法是獲取數值對應的字符串,其中有兩個地方使用了很是巧妙的方式來進行除法運算和取餘運算。在計算機中,a/ba%b相比較位運算,都是比較費時的計算的。下面來看看jdk中是如何優化計算的:

static void getChars(int i, int index, char[] buf) {
        int q, r;
        int charPos = index;
        char sign = 0;

        if (i < 0) {
            sign = '-';
            i = -i;
        }

        // Generate two digits per iteration
        while (i >= 65536) {
            q = i / 100;
        // really: r = i - (q * 100);
            r = i - ((q << 6) + (q << 5) + (q << 2));
            i = q;
            buf [--charPos] = DigitOnes[r];
            buf [--charPos] = DigitTens[r];
        }

        // Fall thru to fast mode for smaller numbers
        // assert(i <= 65536, i);
        for (;;) {
            q = (i * 52429) >>> (16+3);
            r = i - ((q << 3) + (q << 1));  // r = i-(q*10) ...
            buf [--charPos] = digits [r];
            i = q;
            if (i == 0) break;
        }
        if (sign != 0) {
            buf [--charPos] = sign;
        }
    }

其中有一行代碼:

q = (i * 52429) >>> (16+3);

上面這行公式約等於 q * 0.1,也就是說使用乘法計算比使用除法高效。

思路是這樣:

當 i >= 65536時,是每兩位取出數字,i /= 100,例如 i = 567235474,
(1)先取最後兩位 7 和 4 放入buf數組中,i = 5672354,buf = { , , , , , , , '7', '4'};
(2)再取最後兩位 5 和 4 放入buf數組中,i = 56723,buf = { , , , , , '5', '4', '7', '4'};
當 i < 65536 時,跳出循環,採用每一次取出一位數字,也就是 i /= 10
(3)取最後一位 3 放入buf數組中,i = 5672,buf = { , , , , '3', '5', '4', '7', '4'};
(4)取最後一位 2 放入buf數組中,i = 567,buf = { , , , '2', '3', '5', '4', '7', '4'};
(5)取最後一位 7 放入buf數組中,i = 56,buf = { , , '7', '2', '3', '5', '4', '7', '4'};
(6)取最後一位 6 放入buf數組中,i = 5,buf = { , '6', '7', '2', '3', '5', '4', '7', '4'};
(7)取最後一位 5 放入buf數組中,i = 0,buf = { '5', '6', '7', '2', '3', '5', '4', '7', '4'},結束。

總結

關於Integer類暫時介紹到這裏,有關其它的包裝類Long等源碼部分也是相似的,後續就再也不介紹,有興趣的小夥伴能夠去研究一下。

image

相關文章
相關標籤/搜索