掃二維碼與項(xiàng)目經(jīng)理溝通
我們?cè)谖⑿派?4小時(shí)期待你的聲音
解答本文疑問(wèn)/技術(shù)咨詢/運(yùn)營(yíng)咨詢/技術(shù)建議/互聯(lián)網(wǎng)交流
三目運(yùn)算符是我們經(jīng)常在代碼中使用的,a= (b==null?0:1);這樣一行代碼可以代替一個(gè)if-else,可以使代碼變得清爽易讀。

成都創(chuàng)新互聯(lián)主要從事成都網(wǎng)站建設(shè)、網(wǎng)站制作、網(wǎng)頁(yè)設(shè)計(jì)、企業(yè)做網(wǎng)站、公司建網(wǎng)站等業(yè)務(wù)。立足成都服務(wù)金灣,十年網(wǎng)站建設(shè)經(jīng)驗(yàn),價(jià)格優(yōu)惠、服務(wù)專業(yè),歡迎來(lái)電咨詢建站服務(wù):13518219792
但是,三目運(yùn)算符也是有一定的語(yǔ)言規(guī)范的。在運(yùn)用不恰當(dāng)?shù)臅r(shí)候會(huì)導(dǎo)致意想不到的問(wèn)題。本文就介紹一個(gè)我自己曾經(jīng)踩過(guò)的坑。
一、三目運(yùn)算符
對(duì)于條件表達(dá)式b?x:y,先計(jì)算條件b,然后進(jìn)行判斷。如果b的值為true,計(jì)算x的值,運(yùn)算結(jié)果為x的值;否則,計(jì)算y的值,運(yùn)算結(jié)果為y的值。一個(gè)條件表達(dá)式從不會(huì)既計(jì)算x,又計(jì)算y。條件運(yùn)算符是右結(jié)合的,也就是說(shuō),從右向左分組計(jì)算。例如,a?b:c?d:e將按a?b:(c?d:e)執(zhí)行。
二、自動(dòng)裝箱與自動(dòng)拆箱
基本數(shù)據(jù)類型的自動(dòng)裝箱(autoboxing)、拆箱(unboxing)是自J2SE 5.0開(kāi)始提供的功能。
一般我們要?jiǎng)?chuàng)建一個(gè)類的對(duì)象實(shí)例的時(shí)候,我們會(huì)這樣: Class a = new Class(parameters); 當(dāng)我們創(chuàng)建一個(gè)Integer對(duì)象時(shí),卻可以這樣: Integer i = 100;(注意:和 int i = 100;是有區(qū)別的 )
實(shí)際上,執(zhí)行上面那句代碼的時(shí)候,系統(tǒng)為我們執(zhí)行了: Integer i = Integer.valueOf(100); 這里暫且不討論這個(gè)原理是怎么實(shí)現(xiàn)的(何時(shí)拆箱、何時(shí)裝箱),也略過(guò)普通數(shù)據(jù)類型和對(duì)象類型的區(qū)別。
我們可以理解為,當(dāng)我們自己寫的代碼符合裝(拆)箱規(guī)范的時(shí)候,編譯器就會(huì)自動(dòng)幫我們拆(裝)箱。那么,這種不被程序員控制的自動(dòng)拆(裝)箱會(huì)不會(huì)存在什么問(wèn)題呢?
三、問(wèn)題回顧
首先,通過(guò)你已有的經(jīng)驗(yàn)看一下下面這段代碼。如果你得到的結(jié)果和后文分析的結(jié)果一致(并且你知道原理),那么請(qǐng)忽略本文。如果不一致,請(qǐng)跟我探索下去。
- public static void main(String[] args) {
- Map
map = new HashMap<>(); - Boolean b = map != null ? map.get("test") : false;
- System.out.println(b);
- }
以上這段代碼,是我們?cè)诓蛔⒁獾那闆r下有可能經(jīng)常會(huì)寫的一類代碼(在很多時(shí)候我們都愛(ài)使用三目運(yùn)算符)。
一般情況下,我們會(huì)認(rèn)為以上代碼Boolean b的最終得到的值應(yīng)該是null。因?yàn)閙ap.get("test")的值是null,而b又是一個(gè)對(duì)象,所以得到結(jié)果會(huì)是null。
但是,以上代碼會(huì)拋出NPE:
- Exception in thread "main" java.lang.NullPointerException
首先可以明確的是,既然報(bào)了空指針,那么一定是有些地方調(diào)用了一個(gè)null的對(duì)象的某些方法。在這短短的兩行代碼中,看上去只有一處方法調(diào)用map.get("test"),但是我們也都是知道,map已經(jīng)事先初始化過(guò)了,不會(huì)是Null,那么到底是哪里有空指針呢。
我們接下來(lái)反編譯一下該代碼??纯次覀儗懙拇a在經(jīng)過(guò)編譯器處理之后變成了什么樣。反編譯后代碼如下:
- public static void main(String args[]){
- Map map = new HashMap();
- Boolean b = Boolean.valueOf(map == null ? false : ((Boolean)map.get("test")).booleanValue());
- System.out.println(b);
- }
看完這段反編譯之后的代碼之后,經(jīng)過(guò)分析我們大概可以知道問(wèn)題出在哪里。((Boolean)hashmap.get("test")).booleanValue() 的執(zhí)行過(guò)程及結(jié)果如下:
- public static void main(String args[]){
- Map map = new HashMap();
- Boolean b = Boolean.valueOf(map == null ? false : ((Boolean)map.get("test")).booleanValue());
- System.out.println(b);
- }
好,問(wèn)題終于定位到了。很明顯,上面源代碼中的map.get("test")在被編譯成了
(Boolean)map.get("test").booleanValue(),這是一種自動(dòng)拆箱的操作。
那么,為什么這里會(huì)發(fā)生自動(dòng)拆箱呢?這個(gè)問(wèn)題又如何解決呢?
四、原理分析
通過(guò)查看反編譯之后的代碼,我們準(zhǔn)確的定位到了問(wèn)題,分析之后我們可以得出這樣的結(jié)論:NPE的原因應(yīng)該是三目運(yùn)算符和自動(dòng)拆箱導(dǎo)致了空指針異常。
那么,這段代碼為什么會(huì)自動(dòng)拆箱呢?這其實(shí)是三目運(yùn)算符的語(yǔ)法規(guī)范。參見(jiàn)jls-15.25,摘要如下:
簡(jiǎn)單的來(lái)說(shuō)就是:當(dāng)?shù)诙?,第三位操作?shù)分別為基本類型和對(duì)象時(shí),其中的對(duì)象就會(huì)拆箱為基本類型進(jìn)行操作。
所以,結(jié)果就是:由于使用了三目運(yùn)算符,并且第二、第三位操作數(shù)分別是基本類型和對(duì)象。所以對(duì)對(duì)象進(jìn)行拆箱操作,由于該對(duì)象為null,所以在拆箱過(guò)程中調(diào)用null.booleanValue()的時(shí)候就報(bào)了NPE。
五、問(wèn)題解決
如果代碼這么寫,就不會(huì)報(bào)錯(cuò):
- Map
map = new HashMap (); - Boolean b = (map!=null ? map.get("test") : Boolean.FALSE);
就是保證了三目運(yùn)算符的第二第三位操作數(shù)都為對(duì)象類型。這樣就不會(huì)發(fā)生自動(dòng)拆箱操作,以上代碼得到的b的結(jié)果為null。
PS:本文中的示例,只是為了更加方便讀者理解三目運(yùn)算符會(huì)導(dǎo)致自動(dòng)拆箱現(xiàn)象,可能在代碼中并不會(huì)直接這樣使用。但是,我自己的代碼確實(shí)發(fā)生過(guò)類似問(wèn)題。這里簡(jiǎn)化一下,為了講清楚原理。
【本文是專欄作者Hollis的原創(chuàng)文章,作者微信公眾號(hào)Hollis(ID:hollischuang)】

我們?cè)谖⑿派?4小時(shí)期待你的聲音
解答本文疑問(wèn)/技術(shù)咨詢/運(yùn)營(yíng)咨詢/技術(shù)建議/互聯(lián)網(wǎng)交流