通過本質看現象:關於Integer受內部初始化賦值範圍限制而出現的有趣現象
- 2019 年 10 月 5 日
- 筆記
左手程式碼,右手文章。——朱季謙
這是我的第一篇技術部落格,作為一名技術小菜鳥,總體而言顯得很拙見,但也算是成長路上的一個小腳印,希望能在以後的日子裡,可以對JAVA技術有一個更加深入的思考與認識。
前幾天我在逛論壇的時候,偶然看到有人討論這樣一個現象,定義四個Integer類型的變數,分別初始化賦值為a=100,b=100,c=1000,d=1000,然後用println分別列印輸出a==b和c==d的boolean值。這時就會出現一個很有趣的現象,a==b會被判斷為ture,而c==d被判斷為false。我覺得這個問題有點意思,就自己在eclipse上玩了一遍,運行截圖如下:

問題便來了,同樣類型的數值,為何a==b是正確的,而c==d則被判斷為錯誤。在我們現實生活中,人們總說要透過現象去看本質,但若能反過來通過本質來分析現象,我想,同樣可以深入理解很多東西。就像你能讀懂一個人,就會很容易理解這個人的所作所為。打一個比方,你要弄懂一個人為何要犯罪,首先得了解他做這件事的心理,這就是通過本質回過頭去看現象。
這道題,如果能通過本質來看現象,就會茅塞頓開。
Integer的本質是什麼,當然是它的源碼咯。
在我們定義Integer a=100時,編譯器會轉成Integer.valueOf(100),即內部實現是Integer a= Integer.valueOf(100),而在Integer的源碼里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.low和IntegerCache.high。通常情況兩者默認初始化為IntegerCache.high=127,IntegerCache.low=-128,同時,Integer內部還有一個靜態static程式碼塊,它會在類被載入時被執行,該程式碼塊如下:
static { int h = 127; String integerCacheHighPropValue = sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high"); if (integerCacheHighPropValue != null) { try { int i = parseInt(integerCacheHighPropValue); i = Math.max(i, 127); h = Math.min(i, Integer.MAX_VALUE - (-low) -1); } catch( NumberFormatException nfe) { } } 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; }
在執行該靜態程式碼塊時,會新建一個數組cache,把-128到127的數字都放在這裡面。再結合前面的valueOf方法,可以看出,如果賦值的參數在127個-128之間,就會直接從靜態程式碼塊的快取中返回一個實際數,它們都屬於同一個對象;如果超過這個範圍,就會return new Integer(i),即返回一個新建且不同的對象值。
分析完Integer的部分源碼後,就可以知道前面問題為何會出現這樣的現象了。當a,b賦值為100時,兩者都在127~-128的範圍間,在同一個緩衝中,屬於同一個對象且數值相同,那樣a==b即為true;而當c,d賦值為1000時,就超過了範圍,就會創建新的對象,兩個引用指向不同的對象,即使對象擁有相同的內容,用==比較結果依然是false,這樣的話,c,d已不屬於同一個對象了,自然就會為false。