Java源碼閱讀-Integer(基於jdk1.8)

public final class Integer extends Number implements Comparable<Integer>

  Integer 由final修飾了,因此該類不可以被繼承,同時 Integer 繼承了Number類,所以能夠將Integer轉換成 int 、double、float、long、byte和short類型的數據,另外,也實現了comparable接口,所以Integer類也能夠進行天然排序。java

  構造方法只有兩個:git

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

  咱們主要看第二個構造方法,傳入一個字符串,而後調用parseInt方法,接下來進入parseInt的源碼:面試

  

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"); } if (radix < Character.MIN_RADIX) { throw new NumberFormatException("radix " + radix + " less than Character.MIN_RADIX"); } if (radix > Character.MAX_RADIX) { throw new NumberFormatException("radix " + radix + " greater than Character.MAX_RADIX"); } int result = 0; // //是否爲負數 boolean negative = false; int i = 0, len = s.length(); //這裏加個負號是防止數據溢出,int的數值範圍 -2的31次方到2的31次方減一 int limit = -Integer.MAX_VALUE; //最小基數 int multmin; //十進制數字 int digit; if (len > 0) { char firstChar = s.charAt(0); //第一個字符小於0,有多是"-","+"或其餘字符 if (firstChar < '0') { // Possible leading "+" or "-" //爲負數 if (firstChar == '-') { 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++; } /** * 最小基數,主要防止 result *= radix; 這個操做時數據過大 * 致使數據丟失的問題,由於因此帶符號32位int類型整數爲-2147483648~2147483647 */ multmin = limit / radix; while (i < len) { // Accumulating negatively avoids surprises near MAX_VALUE //轉換十進制,這裏獲取的是radix進制下相對應的10進制數字,如: //Character.digit('a',16),則返回 10; //若輸入字符不在進制的範圍以內,則返回 -1: //Character.digit('t',16),返回 -1,Character.digit('a',10),返回 -1 digit = Character.digit(s.charAt(i++),radix); //返回-1說明字符非法 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; }

  這個方法中最核心的步驟是一、result *= radix; 二、result -= digit; 通過這兩個步驟將字符串轉換成數值類型。大概流程是這樣的:數組

  假如字符串"1234" 轉換成int類型,result 的初始值爲0,radix默認爲10;緩存

  首先截取字符串的第一個字符1(這裏忽略各類檢查),通過第一步計算 result = 0*10 = 0;第二部計算 result = 0 - 1 = -1;less

  第一遍循環結束後,result 的值 變成了 -1this

  截取第二個字符 2 ,result = -1 * 10 = -10,result = -10 - 2 = -12;spa

  截取第三個字符 3 ,result = -12 * 10 = -120,result = -120 - 3 = -123;code

  截取第四個字符 4 ,result = -123 * 10 = -1230 ,result = -1230-4 = -1234;orm

   循環結束,此時result的值爲 -1234,完成字符串向整數型的轉換,返回是取反便可。

 

  下面我將從一個面試題引出問題,而後經過閱讀源碼來解決這個問題。

public static void main(String[] args) {
        Integer i1 = 100; Integer i2 = 100; Integer i3 = 200; Integer i4 = 200; Integer i5 = Integer.valueOf(100); Integer i6 = Integer.valueOf(100); Integer i7 = new Integer(100); System.out.println("i1 == i2 的結果是:" + (i1 == i2)); System.out.println("i3 == i4 的結果是:" + (i3 == i4)); System.out.println("i5 == i6 的結果是:" + (i5 == i6)); System.out.println("i1 == i5 的結果是:" + (i1 == i5)); System.out.println("i1 == i7 的結果是:" + (i1 == i7)); }

運行結果爲:

i1 == i2 的結果是:true
i3 == i4 的結果是:false i5 == i6 的結果是:true i1 == i5 的結果是:true i1 == i7 的結果是:false

  咱們先來看第一和第二條結果,一樣是比較兩個相同數值,爲何會有不一樣的結果呢?接下我將經過源碼來解釋緣由。

  首先,咱們經過編譯獲取到class文件的字節碼

  從圖中咱們能夠看到,在執行 Integer i1 = 100 這條命令的時候,編譯器會調用Integer中的靜態方法 valueOf,接下來咱們看看 valueOf方法是怎麼實現的吧。

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

  這個代碼看起來很簡單,Integer 中有一個靜態內部類 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 //緩存範圍最小(也是默認範圍)爲 (-128)~ 127,若是配置java.lang.Integer.IntegerCache.high //high 的值可從配置文件中讀取 int h = 127; String integerCacheHighPropValue = sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high"); if (integerCacheHighPropValue != null) { try { int i = parseInt(integerCacheHighPropValue); //獲取配置文件和127之間的最大值 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; //將數字緩存起來默認 -128 ~ 127 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() {} }

  咱們知道內部類只有在所在類實例化時纔會被實例化,並且只會實例化一次,緩存操做是在靜態代碼塊中完成,也就是說在類被實例化的時候數據就已經被緩存好了,接下使用的時候能夠直接使用緩存的數據。

  如今咱們迴歸到上面的問題,結果1中兩個數據均爲 100,在緩存的範圍中,所以i1和i2都指向的是同一個內存地址,所以返回true。結果2中 兩個數都是200,超出了緩存的範圍,因此直接new 出了兩個對象,所以他們的內存地址不一致,返回結果爲false;另外,使用valueOf 和 使用 = 操做符賦值時同樣的,因此結果3和結果4返回結果爲 true,結果5中 是直接使用new關鍵字建立對象,因此他們的內存地址確定不一致,結果爲false。

  那麼,如今問題又來了,那我怎麼判斷兩個整數的大小呢?繼續看源碼

 
 
/**
* The value of the {@code Integer}.
*
* @serial
*/
private final int value;
public boolean equals(Object obj) {
        if (obj instanceof Integer) { return value == ((Integer)obj).intValue(); } return false; }
public int intValue() {
return value;
}
 

  是的,沒錯,比較兩個數值大小時可使用equals方法來比較,源碼中value的類型爲 int型,intValue返回的也是value,所以能夠判斷兩個數的大小。

public static void main(String[] args) {
        Integer i1 = 200; Integer i2 = 200; System.out.println("i1 == i2 的結果是:" + i1.equals(i2)); //true }

  補充:equals 與 == 的區別:

  equals 比較的是兩個數值的大小,== 有兩種狀況,若是比較的是 基本數據類型,則 == 跟equals同樣都是比較的大小,若是是引用類型或數組,則比較是內存地址。

 

  getChars方法:

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爲後兩位數 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 (;;) { //至關於i*(52429/524288)=i*0.10000038146972656=i*0.1=i/10 //這裏選 52429 和 2的19次方相除,獲得的結果精度更加高,更加接近於 i/10的結果 //之因此要這樣轉換,是由於在計算機運算中位移的效率 > 乘法效率 > 除法效率 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; } }
相關文章
相關標籤/搜索