Integer是平時開發中最經常使用的類之一,可是若是沒有研究過源碼不少特性和坑可能就不知道,下面深刻源碼來分析一下Integer的設計和實現。java
Integer:git
繼承結構:算法
--java.lang.Number緩存
---java.lang.Integerless
其中父類Number是個抽象類,是全部數字類型相關的類的父類,例如Double
、Float
、Integer
、Long
和 Short。
源碼分析
Integer類還實現了Comparable接口用以比較兩個Integer的大小。測試
//源碼 public final class Integer extends Number implements Comparable<Integer>
Integer類中規定了範圍大小時在-2^31~2^31-1之間。this
//源碼 /** * 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字段,表示基本類型 int
的 Class
實例的TYPE字段。spa
內部方法實現:
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類裏還有一些關於反碼、補碼計算等位運算的方法。若是有興趣或者開發中用到再來研究。