JDK源碼分析-Integer


  Integer是平時開發中最常用的類之一,但是如果沒有研究過源碼很多特性和坑可能就不知道,下面深入源碼來分析一下Integer的設計和實現。

Integer:

  繼承結構:

  -java.lang.Object

    --java.lang.Number

      ---java.lang.Integer

  其中父類Number是個抽象類,是所有數字類型相關的類的父類,例如DoubleFloatIntegerLongShort。

  Integer類還實現了Comparable接口用以比較兩個Integer的大小。

//源碼
public final class Integer extends Number implements Comparable<Integer>

  Integer類中規定了范圍大小時在-2^31~2^31-1之間。

    //源碼
    /**
     * A constant holding the minimum value an {@code int} can
     * have, -2<sup>31</sup>.
     */
    @Native public static final int   MIN_VALUE = 0x80000000;

    /**
     * A constant holding the maximum value an {@code int} can
     * have, 2<sup>31</sup>-1.
     */
    @Native public static final int   MAX_VALUE = 0x7fffffff;

  另外還有用來以二進制補碼形式表示 int 值的比特位數的SIZE字段,表示基本類型 intClass 實例的TYPE字段。

  內部方法實現:

  Integer大概實現了四五十個方法,下面結合源碼分析一下平時常用又比較重要的幾個方法。

  首先構造一個Integer對象,Integer的構造方法非常簡單直接傳入一個int或者string即可。傳入int是直接賦值給value字段保存。傳入string是先把s通過parseInt方法轉換成十進制int再賦值給value字段。

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

  接下來看一下這個不簡單的parseInt方法。

  從方法簽名就可以看出這個方法的作用是把傳入的字符串s解析單做radix機制的字串來解析成十進制int值。並進行了一些異常處理。舉個栗子:

     parseInt("0", 10) returns 0
     parseInt("473", 10) returns 473
     parseInt("+42", 10) returns 42
     parseInt("-0", 10) returns 0
     parseInt("-FF", 16) returns -255
     parseInt("1100110", 2) returns 102
     parseInt("2147483647", 10) returns 2147483647
     parseInt("-2147483648", 10) returns -2147483648
     parseInt("2147483648", 10) throws a NumberFormatException
     parseInt("99", 8) throws a NumberFormatException
     parseInt("Kona", 10) throws a NumberFormatException
     parseInt("Kona", 27) returns 411787

  下面來看一下具體實現(為了更清楚的分析實現過程,文字都作為注釋寫在源代碼里了):

  //源碼,限於篇幅簡化了源碼格式。
  public static int parseInt(String s, int radix) throws NumberFormatException {
        //這里有這個警告是因為valueOf方法使用了parseInt方法和IntegerCache對象,
        //因為valueOf在IntegerCache初始化之前使用導致異常情況。后面會詳細分析。
        /*
         * 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用來判斷參數是否合法。radix大小在2~36之間。
        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 limit = -Integer.MAX_VALUE; //最大值限制
        int multmin; //基數下的最小值
        int digit; //記錄每一位的數字

        if (len > 0) {
            char firstChar = s.charAt(0);
            if (firstChar < '0') { // 判斷是否帶‘+’或‘-’
                if (firstChar == '-') {
                    negative = true;
                    limit = Integer.MIN_VALUE;
                } else if (firstChar != '+')
                    throw NumberFormatException.forInputString(s);

                if (len == 1) // 格式非法,含有除了‘+’‘-’之外的字符。
                    throw NumberFormatException.forInputString(s);
                i++;
            }
            multmin = limit / radix;
            while (i < len) {
                //利用了Character類中的digit非法,作用是解析一個字符。
                digit = Character.digit(s.charAt(i++),radix);
                //進行異常判斷。
                //這個解析字符串為數字的算法和平時想到的不太一樣,是從字符串左邊開始,初始化結果是0,
                //其實是把結果算成負的,返回的時候再轉回來。result -= digit;
                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; //如果是負的就直接返回,因為算出來的已經是負數。
    }

  平時經常使用的Integer.parseInt(String s)也是基於這個方法實現的。只不過默認radix為10.

  //源碼    
  public static int parseInt(String s) throws NumberFormatException {
        return parseInt(s,10);
    }

  接下來就來分析一下上面提到的,valueOf方法。一共有三個valueOf方法,只是傳參不同。其中有兩個的內部實現是依據valueOf(int i)和parseInt(String s, int radix)來實現的。

//源碼
public
static Integer valueOf(String s, int radix) throws NumberFormatException { return Integer.valueOf(parseInt(s,radix)); } public static Integer valueOf(String s) throws NumberFormatException { return Integer.valueOf(parseInt(s, 10)); }

那就來分析一下valueOf(int i)方法就好了。

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

可以看到這里使用到了IntegerCache緩存,IntegerCache默認緩存-128~127之間的Integer。IntegerCache是Integer類的靜態內部類。

//源碼
private static class IntegerCache {
    static final int low = -128; //默認low=-128
    static final int high; //high可以配置,通過 VM 參數-XX:AutoBoxCacheMax=<size>
        //high可以配置,所以默認緩存-128~127,但是也可以緩存另外的常用數。
    static final Integer cache[]; //緩存數組

    //靜態代碼塊,Integer類加載時就緩存。
    static {
        // high value may be configured by property
        int h = 127; //默認127
        String integerCacheHighPropValue =
            sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high"); //讀取VM參數配置。
        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; //保證[-128, 127]在緩存范圍內。
    }

    private IntegerCache() {}
}

下面看一段測試代碼:

  
  //首先要明確一點,對象之間的==是比較內存地址,常數之間的比較是數值比較。
  public
static void main(String[] args) { Integer num1 = new Integer(100); Integer num2 = new Integer(100); System.out.println(num1 == num2);//false,因為這兩個對象是獨立創建的,有自己的內存空間和地址。 Integer num3 = 100; Integer num4 = 100; System.out.println(num3 == num4);//true,常數之間比較數值。 Integer num5 = 128; Integer num6 = 128; System.out.println(num5 == num6);//false,自動裝箱成對象,但是超過了默認的緩存范圍,同第一個。如果是127就是true。 Integer num7 = 100; Integer num8 = new Integer(100); System.out.println(num7 == num8);//false,兩個對象之間比較內存地址,不同的是num7通過自動裝箱調用valueOf方法,指向緩存的100,而num8是指向自己內存空間里的100. int num9 = 100; Integer num10 = new Integer(100);
     System.out.println(num9 == num10);//true,Integer對象和int比較時,Integer會自動拆箱(intValue方法)成為int,變成兩個數值比較。 Integer num11
= 100; System.out.println(num9 == num11);//true,num11通過自動裝箱調用valueOf方法指向緩存中的100,比較的時候緩存中的100對象自動拆箱成為數值100. }

  如果沒有認真研究過Integer的緩存機制和自動拆箱裝箱機制的話,這個程序的運行結果絕對會讓你出乎意料。理解之后就OK了。

  理解這個緩存機制也是非常重要的,因為如果程序中因為這個出現了bug那么如果不知道緩存機制估計到死也調不出來。

  這里說一下關於Long,Short是和Integer機制類似,只不過不支持high的配置。Double,Float是沒有緩存機制的,因為即使是-128~127之間的浮點數接近無窮大。

  這一次的Integer類的源碼分析就到這里,Integer類里還有一些關於反碼、補碼計算等位運算的方法。如果有興趣或者開發中用到再來研究。

 


免責聲明!

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



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