掃二維碼與項目經(jīng)理溝通
我們在微信上24小時期待你的聲音
解答本文疑問/技術(shù)咨詢/運營咨詢/技術(shù)建議/互聯(lián)網(wǎng)交流
最近,阿里巴巴Java開發(fā)手冊發(fā)布了最新版,泰山版,這個名字起的不錯,一覽眾山小。

城固ssl適用于網(wǎng)站、小程序/APP、API接口等需要進行數(shù)據(jù)傳輸應(yīng)用場景,ssl證書未來市場廣闊!成為創(chuàng)新互聯(lián)公司的ssl證書銷售渠道,可以享受市場價格4-6折優(yōu)惠!如果有意向歡迎電話聯(lián)系或者加微信:18982081108(備注:SSL證書合作)期待與您的合作!
新版據(jù)說新增了30+規(guī)約,我還沒來得及仔細去看,不過有粉絲和我說,其中新增的一條規(guī)約,他之前在我的博客中看到過。
仔細看了下,這個問題確實我很久之前遇到過,確實曾經(jīng)在博客中也記錄過。
最初遇到這個問題的是我的同事,他在代碼中使用了三目運算符,代碼在線上運行的時候發(fā)生了NPE,經(jīng)過排查,發(fā)現(xiàn)原來是三目運算符和自動拆裝箱之間有一定的關(guān)系,導(dǎo)致了空指針。
這篇文章最開始發(fā)布于2015年,目前已經(jīng)有1w+閱讀量了。
趁著最新的開發(fā)手冊中也提到了這個點,于是把之前的文章內(nèi)容翻出來并重新整理了一下,帶大家一起回顧下這個知識點。
一、三目運算符
對于條件表達式b?x:y,先計算條件b,然后進行判斷。如果b的值為true,計算x的值,運算結(jié)果為x的值;否則,計算y的值,運算結(jié)果為y的值。一個條件表達式從不會既計算x,又計算y。條件運算符是右結(jié)合的,也就是說,從右向左分組計算。例如,a?b:c?d:e將按a?b:(c?d:e)執(zhí)行。
二、自動裝箱與自動拆箱
基本數(shù)據(jù)類型的自動裝箱(autoboxing)、拆箱(unboxing)是自J2SE 5.0開始提供的功能。
一般我們要創(chuàng)建一個類的對象實例的時候,我們會這樣:Class a = new Class(parameters);
當我們創(chuàng)建一個Integer對象時,卻可以這樣:Integer i = 100;(注意:和 int i = 100;是有區(qū)別的)
實際上,執(zhí)行上面那句代碼的時候,系統(tǒng)為我們執(zhí)行了:Integer i = Integer.valueOf(100);
這里暫且不討論這個原理是怎么實現(xiàn)的(何時拆箱、何時裝箱),也略過普通數(shù)據(jù)類型和對象類型的區(qū)別。
我們可以理解為,當我們自己寫的代碼符合裝(拆)箱規(guī)范的時候,編譯器就會自動幫我們拆(裝)箱。
那么,這種不被程序員控制的自動拆(裝)箱會不會存在什么問題呢?
三、問題回顧
首先,通過你已有的經(jīng)驗看一下下面這段代碼:
- Map
map = new HashMap (); - Boolean b = (map!=null ? map.get("test") : false);
以上這段代碼,是我們在不注意的情況下有可能經(jīng)常會寫的一類代碼(在很多時候我們都愛使用三目運算符)。當然,這段代碼是存在問題的,執(zhí)行該代碼,會報NPE.
- HashMap hashmap = new HashMap();
- Boolean boolean1 = Boolean.valueOf(hashmap == null ? false : ((Boolean)hashmap.get("test")).booleanValue());
首先可以明確的是,既然報了空指針,那么一定是有些地方調(diào)用了一個null的對象的某些方法。
在這短短的兩行代碼中,看上去只有一處方法調(diào)用map.get("test"),但是我們也都是知道,map已經(jīng)事先初始化過了,不會是Null,那么到底是哪里有空指針呢。
我們接下來反編譯一下該代碼??纯次覀儗懙拇a在經(jīng)過編譯器處理之后變成了什么樣。
反編譯后代碼如下:
- HashMap hashmap = new HashMap();
- Boolean boolean1 = Boolean.valueOf(hashmap == null ? false : ((Boolean)hashmap.get("test")).booleanValue());
看完這段反編譯之后的代碼之后,經(jīng)過分析我們大概可以知道問題出在哪里。
((Boolean)hashmap.get("test")).booleanValue()的執(zhí)行過程及結(jié)果如下:
- hashmap.get("test")->null;
- (Boolean)null->null;
- null.booleanValue()->報錯
好,問題終于定位到了。那么接下來看看如何解決該問題以及為什么會出現(xiàn)這種問題。
四、原理分析
通過查看反編譯之后的代碼,我們準確的定位到了問題,分析之后我們可以得出這樣的結(jié)論:NPE的原因應(yīng)該是三目運算符和自動拆箱導(dǎo)致了空指針異常。
根據(jù)規(guī)定,三目運算符的第二、第三位操作數(shù)的返回值類型應(yīng)該是一樣的,這樣才能當把一個三目運算符的結(jié)果賦值給一個變量。
如:Person i = a>b ? i1:i2; ,就要求i1和i2的類型都必須是Person才行。
因為Java中存在一種特殊的情況,那就是基本數(shù)據(jù)類型和包裝數(shù)據(jù)類型可以通過自動拆裝箱的方式互相轉(zhuǎn)換。即可以定義int i = new Integer(10);也可以定義Integer i= 10;
那如果,三目運算符的第二位和第三位的操作數(shù)的類型分別是基本數(shù)據(jù)類型和包裝類型對象時,就需要有一方需要進行自動拆裝箱。
那到底如何做的呢,根據(jù)三目運算符的語法規(guī)范。參見jls-15.25,摘要如下:
簡單的來說就是:當?shù)诙?,第三位操作?shù)分別為基本類型和對象時,其中的對象就會拆箱為基本類型進行操作。
所以,結(jié)果就是:由于使用了三目運算符,并且第二、第三位操作數(shù)分別是基本類型和對象。所以對對象進行拆箱操作,由于該對象為null,所以在拆箱過程中調(diào)用null.booleanValue()的時候就報了NPE。
五、問題解決
如果代碼這么寫,就不會報錯:
- Map
map = new HashMap (); - Boolean b = (map!=null ? map.get("test") : Boolean.FALSE);
就是保證了三目運算符的第二第三位操作數(shù)都為對象類型。
這和三目運算符有關(guān)。
關(guān)于作者:Hollis,一個對Coding有著獨特追求的人,現(xiàn)任阿里巴巴技術(shù)專家,個人技術(shù)博主,技術(shù)文章全網(wǎng)閱讀量數(shù)千萬,《程序員的三門課》聯(lián)合作者。
【本文是專欄作者Hollis的原創(chuàng)文章,作者微信公眾號Hollis(ID:hollischuang)】

我們在微信上24小時期待你的聲音
解答本文疑問/技術(shù)咨詢/運營咨詢/技術(shù)建議/互聯(lián)網(wǎng)交流