Java的自動拆箱和裝箱是Java語言的一顆語法糖。在以前的學習中有不少誤解,在別人的幫助下做出一些修正。先看下面的代碼:java
1 public static void main(String args[]) { 2 Integer a = 1; 3 Integer b = 2; 4 Integer c = 3; 5 Integer d = 3; 6 Integer e = 321; 7 Integer f = 321; 8 Long g = 3L; 9 int x = 3; 10 long y = 3L; 11 12 //x,y雖然類型不一樣可是能夠直接進行數值比較 13 System.out.println(x == y); 14 //System.out.println(c == g); 提示出錯,不可比較的類型。說明此時沒有自動拆箱 15 System.out.println(c == d); 16 System.out.println(e == f); 17 System.out.println(c == (a+b)); 18 System.out.println(c.equals(a+b)); 19 //此時進行了自動的拆箱 20 System.out.println(g == (a+b)); 21 System.out.println(g.equals(a+b)); 22 }
答案是:數組
T緩存
T學習
Fspa
Tcode
Torm
T對象
Fblog
這樣的答案是否是出乎不少人的意料呢?咱們一一來分析。繼承
1. 首先咱們明確一下"=="和equals方法的做用。
"==":若是是基本數據類型,則直接對值進行比較,若是是引用數據類型,則是對他們的地址進行比較(可是隻能比較相同類型的對象,或者比較父類對象和子類對象。類型不一樣的兩個對象不能使用==)
equals方法繼承自Object類,在具體實現時能夠覆蓋父類中的實現。看一下Object中qeuals的源碼發現,它的實現也是對對象的地址進行比較,此時它和"=="的做用相同。而JDK類中有一些類覆蓋了Object類的equals()方法,比較規則爲:若是兩個對象的類型一致,而且內容一致,則返回true,這些類有:
java.io.file,java.util.Date,java.lang.string,包裝類(Integer,Double等)。
2. Java的包裝類實現細節。觀察源碼會發現Integer包裝類中定義了一個私有的靜態內部類以下:
1 private static class IntegerCache { 2 static final int low = -128; 3 static final int high; 4 static final Integer cache[]; 5 6 static { 7 // high value may be configured by property 8 int h = 127; 9 String integerCacheHighPropValue = 10 sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high"); 11 if (integerCacheHighPropValue != null) { 12 try { 13 int i = parseInt(integerCacheHighPropValue); 14 i = Math.max(i, 127); 15 // Maximum array size is Integer.MAX_VALUE 16 h = Math.min(i, Integer.MAX_VALUE - (-low) -1); 17 } catch( NumberFormatException nfe) { 18 // If the property cannot be parsed into an int, ignore it. 19 } 20 } 21 high = h; 22 23 cache = new Integer[(high - low) + 1]; 24 int j = low; 25 for(int k = 0; k < cache.length; k++) 26 cache[k] = new Integer(j++); 27 28 // range [-128, 127] must be interned (JLS7 5.1.7) 29 assert IntegerCache.high >= 127; 30 } 31 32 private IntegerCache() {} 33 }
而Integer的自動裝箱代碼:
1 public static Integer valueOf(int i) { 2 if (i >= IntegerCache.low && i <= IntegerCache.high) 3 return IntegerCache.cache[i + (-IntegerCache.low)]; 4 return new Integer(i); 5 }
經過觀察上面的代碼咱們能夠發現,Integer使用一個內部靜態類中的一個靜態數組保存了-128-127範圍內的數據,靜態數組在類加載之後是存在方法區的,並非什麼常量池。在自動裝箱的時候,首先判斷要裝箱的數字的範圍,若是在-128-127的範圍則直接返回緩存中已有的對象,不然new一個新的對象。其餘的包裝類也有相似的實現方式,能夠經過源碼觀察一下。
3. "=="在遇到非算術運算符的狀況下不會自動拆箱,以及他們的equals方法不處理數據類型轉換的關係。
所以,對於 System.out.println(c == d); 他們指向同一個對象,返回True。
對於 System.out.println(e == f); 他們的值大於127,即便值相同,可是對應不一樣的內存地址,返回false。
對於 System.out.println(c == (a+b)); 自動拆箱後他們的值是相等的,返回True。
對於 System.out.println(c.equals(a+b)); 他們的值相同,並且類型相同,返回true。
對於 System.out.println(g == (a+b)); 自動拆箱後他們的值相等,返回True。
對於 System.out.println(g.equals(a+b)); 他們的值相同可是類型不一樣,返回false。
4. 總結
對於不懂的地方,最好是經過閱讀源碼的方式來解決。這樣才能真正明白內部的一些實現方式。