三目運(yùn)算符是我們經(jīng)常在代碼中使用的,a=(b==null?0:1);這樣一行代碼可以代替一個if-else,可以使代碼變得清爽易讀。但是,三目運(yùn)算符也是有一定的語言規(guī)范的。在運(yùn)用不恰當(dāng)?shù)臅r候會導(dǎo)致意想不到的問題。
一、三目運(yùn)算符
對于條件表達(dá)式b?x:y,先計(jì)算條件b,然后進(jìn)行判斷。如果b的值為true,計(jì)算x的值,運(yùn)算結(jié)果為x的值;否則,計(jì)算y的值,運(yùn)算結(jié)果為y的值。一個條件表達(dá)式從不會既計(jì)算x,又計(jì)算y。條件運(yùn)算符是右結(jié)合的,也就是說,從右向左分組計(jì)算。例如,a?b:c?d:e將按a?b:(c?d:e)執(zhí)行。
二、自動裝箱與自動拆箱
基本數(shù)據(jù)類型的自動裝箱(autoboxing)、拆箱(unboxing)是自J2SE5.0開始提供的功能。
一般我們要創(chuàng)建一個類的對象實(shí)例的時候,我們會這樣:Classa=newClass(parameters);當(dāng)我們創(chuàng)建一個Integer對象時,卻可以這樣:Integeri=100;(注意:和inti=100;是有區(qū)別的)
實(shí)際上,執(zhí)行上面那句代碼的時候,系統(tǒng)為我們執(zhí)行了:Integeri=Integer.valueOf(100);這里暫且不討論這個原理是怎么實(shí)現(xiàn)的(何時拆箱、何時裝箱),也略過普通數(shù)據(jù)類型和對象類型的區(qū)別。
我們可以理解為,當(dāng)我們自己寫的代碼符合裝(拆)箱規(guī)范的時候,編譯器就會自動幫我們拆(裝)箱。那么,這種不被程序員控制的自動拆(裝)箱會不會存在什么問題呢?
三、問題回顧
首先,通過你已有的經(jīng)驗(yàn)看一下下面這段代碼。如果你得到的結(jié)果和后文分析的結(jié)果一致(并且你知道原理),那么請忽略本文。如果不一致,請跟我探索下去。
publicstaticvoidmain(String[]args){
Map<String,Boolean>map=newHashMap<>();
Booleanb=map!=null?map.get("test"):false;
System.out.println(b);
}
以上這段代碼,是我們在不注意的情況下有可能經(jīng)常會寫的一類代碼(在很多時候我們都愛使用三目運(yùn)算符)。
一般情況下,我們會認(rèn)為以上代碼Booleanb的最終得到的值應(yīng)該是null。因?yàn)閙ap.get("test")的值是null,而b又是一個對象,所以得到結(jié)果會是null。
但是,以上代碼會拋出NPE:
Exceptioninthread"main"java.lang.NullPointerException
首先可以明確的是,既然報了空指針,那么一定是有些地方調(diào)用了一個null的對象的某些方法。在這短短的兩行代碼中,看上去只有一處方法調(diào)用map.get("test"),但是我們也都是知道,map已經(jīng)事先初始化過了,不會是Null,那么到底是哪里有空指針呢。
我們接下來反編譯一下該代碼??纯次覀儗懙拇a在經(jīng)過編譯器處理之后變成了什么樣。反編譯后代碼如下:
publicstaticvoidmain(Stringargs[]){
Mapmap=newHashMap();
Booleanb=Boolean.valueOf(map==null?false:((Boolean)map.get("test")).booleanValue());
System.out.println(b);
}
看完這段反編譯之后的代碼之后,經(jīng)過分析我們大概可以知道問題出在哪里。((Boolean)hashmap.get("test")).booleanValue()的執(zhí)行過程及結(jié)果如下:
hashmap.get("test")->null;
(Boolean)null->null;
null.booleanValue()->報錯
好,問題終于定位到了。很明顯,上面源代碼中的map.get("test")在被編譯成了
(Boolean)map.get("test").booleanValue(),這是一種自動拆箱的操作。
那么,為什么這里會發(fā)生自動拆箱呢?這個問題又如何解決呢?
四、原理分析
通過查看反編譯之后的代碼,我們準(zhǔn)確的定位到了問題,分析之后我們可以得出這樣的結(jié)論:NPE的原因應(yīng)該是三目運(yùn)算符和自動拆箱導(dǎo)致了空指針異常。
那么,這段代碼為什么會自動拆箱呢?這其實(shí)是三目運(yùn)算符的語法規(guī)范。參見jls-15.25,摘要如下:
Ifthesecondandthirdoperandshavethesametype(whichmaybethenulltype),thenthatisthetypeoftheconditionalexpression.
IfoneofthesecondandthirdoperandsisofprimitivetypeT,andthetypeoftheotheristheresultofapplyingboxingconversion(§5.1.7)toT,thenthetypeoftheconditionalexpressionisT.
Ifoneofthesecondandthirdoperandsisofthenulltypeandthetypeoftheotherisareferencetype,thenthetypeoftheconditionalexpressionisthatreferencetype.
簡單的來說就是:當(dāng)?shù)诙谌徊僮鲾?shù)分別為基本類型和對象時,其中的對象就會拆箱為基本類型進(jìn)行操作。
所以,結(jié)果就是:由于使用了三目運(yùn)算符,并且第二、第三位操作數(shù)分別是基本類型和對象。所以對對象進(jìn)行拆箱操作,由于該對象為null,所以在拆箱過程中調(diào)用null.booleanValue()的時候就報了NPE。
五、問題解決
如果代碼這么寫,就不會報錯:
Map<String,Boolean>map=newHashMap<String,Boolean>();
Booleanb=(map!=null?map.get("test"):Boolean.FALSE);
就是保證了三目運(yùn)算符的第二第三位操作數(shù)都為對象類型。這樣就不會發(fā)生自動拆箱操作,以上代碼得到的b的結(jié)果為null。
以上就是長沙達(dá)內(nèi)教育Java培訓(xùn)機(jī)構(gòu)小編介紹的“Javase全套視頻:三目運(yùn)算符”的內(nèi)容,希望對大家有幫助,如有疑問,請?jiān)诰€咨詢,有專業(yè)老師隨時為你服務(wù)。