The Integer class wraps a value of the primitive type int in an object. An object of type Integer contains a single field whose type is int.
Integer 類的介紹很簡單癞志,使用也一樣簡單簿寂,一般使用也不會出現(xiàn)什么問題,當(dāng)然文章標題既然是踩坑系列禽炬,就說說自己工作中遇到過的關(guān)于Integer的坑吧孩哑。
背景:上家公司是傳統(tǒng)行業(yè),所在的部門開發(fā)了一款法院庭審系統(tǒng),新版本發(fā)布前都經(jīng)過嚴格的測試(考慮到軟件的應(yīng)用現(xiàn)場是法院等一些國家單位基于數(shù)據(jù)保密性等的原因現(xiàn)場測試難度較大)蓝撇,由于從公司版本發(fā)布到真正軟件在現(xiàn)場環(huán)境運行可能是N個月之后,出現(xiàn)問題也難于排查陈莽,只能在公司環(huán)境進行復(fù)現(xiàn)渤昌,然后修改BUG。
問題出現(xiàn):軟件部署到現(xiàn)場走搁,經(jīng)過演示独柑,測試一切順利,法院開始使用私植,但是使用一段時間之后出現(xiàn)無法開庭的情況忌栅。
問題定位:進過調(diào)試,復(fù)現(xiàn)曲稼,最終問題定位在Integer比較相等的一段代碼上索绪。類似于下面的這段代碼:
if(userLogin.getId() == userSjy.getId()){ // 允許開庭 }
邏輯很簡單,就是當(dāng)前的用戶id和書記員id相等的時候允許開庭贫悄,否則不允許開庭瑞驱。而這里的User.id 的類型是Integer,或許你會說很明顯應(yīng)該用 equals() 方法窄坦,但就是有人寫了這樣的代碼唤反,而且沒有測試出來凳寺,發(fā)布了,而且在現(xiàn)場運行了一段時間之后才爆出了問題彤侍。
問題反思:問題是詭異之處在于肠缨,系統(tǒng)剛開始是正常的,經(jīng)過一段時間的運行才出現(xiàn)問題盏阶。說明有什么東西發(fā)生了改變晒奕,而發(fā)生改變的東西顯然不是代碼本身,那到底是什么發(fā)生改變導(dǎo)致了這樣詭異的的BUG呢名斟?問題出在Integer本身脑慧。請看如下代碼:
Integer a = 1;
Integer b = 1;
System.out.println(a.equals(b)); // true
System.out.println(a==b); // true
還有如下代碼:
Integer a = 127;
Integer b = 127;
System.out.println(a.equals(b)); // true
System.out.println(a==b); // true
最后如下代碼:
Integer a = 127+1;
Integer b = 127+1;
System.out.println(a.equals(b)); // true
System.out.println(a==b); // false ? <<---
出現(xiàn)了詭異的 ?false ,而且數(shù)值定位在了 127+1蒸眠,而這里到底發(fā)生了什么漾橙?為什么會出現(xiàn)這樣的現(xiàn)象?
問題原因:我們使用Java自帶的反編譯工具對這段代碼進行反編譯楞卡,看看Java編譯器對這段代碼做了什么霜运,命令行輸入
javap -v ?IntegerTest.class
跳過棧信息,輸出如下:
最重要的信息:
0: sipush ? ? ? ?128
3: invokestatic ?#16 ? ? ? // Method java/lang/Integer.valueOf(I)Ljava/lang/Integer;
6: astore_1
7: sipush ? ? ? ?128
10: invokestatic ?#16 ? ? ?// Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
翻譯一下:
Integer a = 127+1; ?// Integer a = Integer.valueOf(128);
Integer b = 127+1; // Integer b = Integer.valueOf(128);
所以蒋腮,看似你直接給 a 和 b 賦值 淘捡,其實是編譯器幫你做了一些事情,調(diào)用了 Integer.valueOf() 方法池摧,而在Integer.valueOf()方法里面到底發(fā)生了什么呢焦除?
IntegerCache 是什么?顯然它是Java為了提高性能所做的一種緩存機制作彤,緩存的下界是-128它是final的膘魄,默認的上界是127不是final的,JVM啟動的時候就把這些值緩存起來了竭讳,看代碼貌似上界是可以設(shè)置下界的值创葡?
當(dāng)然可以改變緩存值的上界,為什么下界無法改變绢慢?可能是因為沒必要吧灿渴,因為負數(shù)使用的頻率確實很低不是么?JVM 啟動參數(shù)決定緩存的上界大小胰舆。
而很顯然骚露,啟動參數(shù)的名稱沒有寫成 IntegerAutoBoxCacheMax,說明是全局的參數(shù)缚窿,奇怪的是看了下Long的源代碼貌似并沒有使用這個參數(shù) ~_~ 棘幸,而是寫死了-128~127
問題最終原因算是找到了,因為Java對小數(shù)值的Integer進行了緩存(使用頻率高)滨攻,以提高Java的效率够话,造成的現(xiàn)象就是在系統(tǒng)運行的起初階段(包括測試階段)在書記員id還很小的時候蓝翰,== 的效果和equals一樣光绕,而后來隨著id的不斷增大(也可能書記員id不會超過127女嘲,問題就一直不暴露出來),達到128乃至更大的的時候诞帐,問題就必定暴露出來欣尼。
總結(jié):當(dāng)然這個坑算是填上了,在比較Integer/Long 等基本類型包裝類是否相等的的時候的時候不要直接使用==去比較停蕉,而要使用equals方法愕鼓。說的再籠統(tǒng)一點就是比較對象類型相等的時候請使用equals方法。