Hello~各位讀者新年好!不知道大家春節假期是否已延長,小黑哥剛接到通知,假期延長到 2 月 2 號,另外回去之後需要在家辦公,自行隔離兩週。還沒試過在家辦公,小黑哥就怕到時候生物鐘還沒調整過來,一覺睡醒已經是下午了。。。
前言
春節假期,還躺在牀上小黑哥,收到對賬系統的一條預警短信,提示當前系統資金覈對存在問題。關於資金的問題,都是大問題,小黑哥連忙拔出電腦,連上 VPN,登錄生產環境的查看相關日誌。
通過日誌,很快小黑哥定位到相關代碼。
有的同學可能一下子就能看出這裏的問題, Long
對象採用 !=
進行比較,這真是一個低級 Bug
。幸好 Leader 還不知道,趕緊悄悄修復一下。
現在回想小黑哥當初寫這段代碼的時候,誤以爲兩個 Long
對象比較將會進行自動拆箱,轉變爲兩個基本數值類型比較。
下面開始複習一下 Java
自動裝箱與拆箱機制。
自動裝箱與拆箱機制
自動裝箱(Autoboxing),是 JDK5
新增的一種語法糖,將會在代碼編譯時自動將原始類型轉換爲其對應的對象包裝器類。例如將 int
轉換爲 Integer
,double
轉換爲 Double
。如果轉換結果相反,我們就將其稱爲拆箱。
下面是一個自動裝箱的例子:
上面代碼 li.add(i)
就發生自動裝箱,將基本數據類型 long
轉換爲其包裝類 Long
。
查看這段代碼對應的字節碼。
圖上黃線標註的字節碼對應的代碼爲 li.add(i)
。從這我們可以看到 long
類型的自動裝箱實際上調用 Long#valueOf
方法。所以編譯器運行時將之前的代碼轉換爲下面的代碼
接下來我們來看一個自動拆箱的例子:
由於 Long
包裝類對象不能用於求模(%
)以及 =
,所以這段代碼將會發生自動拆箱,將 Long
轉化爲 long
類型。相應的這裏我們也看下其編譯之後的字節碼。
這裏的字節碼比之前的複雜很多,這裏主要關注黃線部分。可以看到這裏調用 Long#longValue
將 Long
對象轉爲 long
類型。所以自動拆箱這個例子,最後編譯器生成字節碼等同於以下代碼:
Java
規定的 8 種數據類型都有其對應包裝類,這些都可以進行相應的自動裝箱和拆箱。
這裏小結一下:
- 自動裝箱機制是通過調用包裝器類
valueOf
實現 - 自動拆箱機制通過調用包裝器類的相應的
**Value
,如longValue
,intValue
實現
Cache 陷阱
自動裝箱和拆箱概念說起其實挺簡單的,但是如果使用不當可能就會踩坑。
我們來看一段代碼:
如果你對上面的結果的不是很清楚,恭喜你,暖男小黑哥幫你排雷了。
上面輸出結果爲:
true
false
這裏輸出結果之所以爲這樣,主要與 LongCache
有關。自動裝箱進制將會調用 Long#valueOf
,其源碼如下:
只要數值範圍位於 [-128,127] 之間,valueOf
就會返回 LongCache.cache
這個數組中的值。所以 aLong/bLong
其實是同一個對象,cLong/dLong
是兩個不用對象的。
Long#valueOf
這個方法通過 Cache
減少創建對象的數量,提高相應的空間以及時間性能。
至於爲什麼緩存 [-128,127] 之間的數字,而沒有緩存更多的值,甚至緩存所有的值?
這是因爲 Long
範圍爲**[-263,263]**,總共 2^64 ,這麼多對象實例,顯然不宜全部緩存。至於 [-128,127] 之間數字 JDK
設計者認爲這些數字使用頻率高,個人認爲這是一個經驗值。
另外這個 cache
可能會導致另外一個問題:鎖共用。
上面代碼看起來 A
類使用 along
這個對象鎖,而 B
類使用 blong
這個對象鎖,看起來兩個風馬牛不相及,但是實際上兩個對象由於 Cache
機制,導致其實際上使用同一個對象,A
與 B
共用同一把鎖。
除了 Long#valueOf
方法中存在 Cache
以外,Byte
,Short
,Integer
,Character
也存在相應的 Cache
值。其中 Integer
對象可以通過 -XX:AutoBoxCacheMax=<size>
改變 Cache
初始範圍。
技術總結
通過自動裝箱與拆箱機制,大大減少了數據類型轉化的代碼,但是同時帶來一些隱藏的問題。這裏我們需要記住:
- 所有對象之間不能使用
==
比較,需要使用equals
代替,推薦使用JDK7
提供的Objects#equals
方法,該方法可以有效避免比較過程空指針問題 - 基本數據類型的包裝類不適合當做鎖對象
各位讀者朋友們,請牢記這兩個總結,不要踩中這兩個坑哦。另外推薦大家安裝一下 FindBugs
這個插件,通過這個插件掃描代碼,可以找出代碼中的相關缺陷。上面兩個問題,通過 FindBugs
都能掃描出來,簡直神器。
對象比較問題:
鎖問題
幫助文檔
- Autoboxing and Unboxing『https://docs.oracle.com/javase/tutorial/java/data/autoboxing.html』
- 極客時間-『Java併發編程實戰』專欄
歡迎關注我的公衆號:程序通事,獲得日常乾貨推送。如果您對我的專題內容感興趣,也可以關注我的博客:studyidea.cn