摘要:但是,三目運(yùn)算符也是有一定的語言規(guī)范的。一三目運(yùn)算符對于條件表達(dá)式,先計(jì)算條件,然后進(jìn)行判斷。那么,這段代碼為什么會自動拆箱呢這其實(shí)是三目運(yùn)算符的語法規(guī)范。所以,結(jié)果就是由于使用了三目運(yùn)算符,并且第二第三位操作數(shù)分別是基本類型和對象。
三目運(yùn)算符是我們經(jīng)常在代碼中使用的,a= (b==null?0:1);這樣一行代碼可以代替一個(gè)if-else,可以使代碼變得清爽易讀。
但是,三目運(yùn)算符也是有一定的語言規(guī)范的。在運(yùn)用不恰當(dāng)?shù)臅r(shí)候會導(dǎo)致意想不到的問題。本文就介紹一個(gè)我自己曾經(jīng)踩過的坑。
一、三目運(yùn)算符
對于條件表達(dá)式b?x:y,先計(jì)算條件b,然后進(jìn)行判斷。如果b的值為true,計(jì)算x的值,運(yùn)算結(jié)果為x的值;否則,計(jì)算y的值,運(yùn)算結(jié)果為y的值。一個(gè)條件表達(dá)式從不會既計(jì)算x,又計(jì)算y。條件運(yùn)算符是右結(jié)合的,也就是說,從右向左分組計(jì)算。例如,a?b:c?d:e將按a?b:(c?d:e)執(zhí)行。
二、自動裝箱與自動拆箱
基本數(shù)據(jù)類型的自動裝箱(autoboxing)、拆箱(unboxing)是自J2SE 5.0開始提供的功能。
一般我們要創(chuàng)建一個(gè)類的對象實(shí)例的時(shí)候,我們會這樣: Class a = new Class(parameters); 當(dāng)我們創(chuàng)建一個(gè)Integer對象時(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í)裝箱),也略過普通數(shù)據(jù)類型和對象類型的區(qū)別。
我們可以理解為,當(dāng)我們自己寫的代碼符合裝(拆)箱規(guī)范的時(shí)候,編譯器就會自動幫我們拆(裝)箱。那么,這種不被程序員控制的自動拆(裝)箱會不會存在什么問題呢?
三、問題回顧
首先,通過你已有的經(jīng)驗(yàn)看一下下面這段代碼。如果你得到的結(jié)果和后文分析的結(jié)果一致(并且你知道原理),那么請忽略本文。如果不一致,請跟我探索下去。
public static void main(String[] args) { Mapmap = new HashMap<>(); Boolean b = map != null ? map.get("test") : false; System.out.println(b); }
以上這段代碼,是我們在不注意的情況下有可能經(jīng)常會寫的一類代碼(在很多時(shí)候我們都愛使用三目運(yùn)算符)。
一般情況下,我們會認(rèn)為以上代碼Boolean b的最終得到的值應(yīng)該是null。因?yàn)閙ap.get("test")的值是null,而b又是一個(gè)對象,所以得到結(jié)果會是null。
但是,以上代碼會拋出NPE:
Exception in thread "main" java.lang.NullPointerException
首先可以明確的是,既然報(bào)了空指針,那么一定是有些地方調(diào)用了一個(gè)null的對象的某些方法。在這短短的兩行代碼中,看上去只有一處方法調(diào)用map.get("test"),但是我們也都是知道,map已經(jīng)事先初始化過了,不會是Null,那么到底是哪里有空指針呢。
我們接下來[反編譯]一下該代碼??纯次覀儗懙拇a在經(jīng)過編譯器處理之后變成了什么樣。反編譯后代碼如下:
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)過分析我們大概可以知道問題出在哪里。((Boolean)hashmap.get("test")).booleanValue() 的執(zhí)行過程及結(jié)果如下:
hashmap.get("test")->null; (Boolean)null->null; null.booleanValue()->報(bào)錯
好,問題終于定位到了。很明顯,上面源代碼中的map.get("test")在被編譯成了
(Boolean)map.get("test").booleanValue(),這是一種自動拆箱的操作。
那么,為什么這里會發(fā)生自動拆箱呢?這個(gè)問題又如何解決呢?
四、原理分析
通過查看反編譯之后的代碼,我們準(zhǔn)確的定位到了問題,分析之后我們可以得出這樣的結(jié)論:NPE的原因應(yīng)該是三目運(yùn)算符和自動拆箱導(dǎo)致了空指針異常。
那么,這段代碼為什么會自動拆箱呢?這其實(shí)是三目運(yùn)算符的語法規(guī)范。參見jls-15.25,摘要如下:
If the second and third operands have the same type (which may be the null type), then that is the type of the conditional expression.
If one of the second and third operands is of primitive type T, and the type of the other is the result of applying boxing conversion (§5.1.7) to T, then the type of the conditional expression is T.
If one of the second and third operands is of the null type and the type of the other is a reference type, then the type of the conditional expression is that reference type.
簡單的來說就是:當(dāng)?shù)诙谌徊僮鲾?shù)分別為基本類型和對象時(shí),其中的對象就會拆箱為基本類型進(jìn)行操作。
所以,結(jié)果就是:由于使用了三目運(yùn)算符,并且第二、第三位操作數(shù)分別是基本類型和對象。所以對對象進(jìn)行拆箱操作,由于該對象為null,所以在拆箱過程中調(diào)用null.booleanValue()的時(shí)候就報(bào)了NPE。
五、問題解決
如果代碼這么寫,就不會報(bào)錯:
Mapmap = new HashMap (); Boolean b = (map!=null ? map.get("test") : Boolean.FALSE);
就是保證了三目運(yùn)算符的第二第三位操作數(shù)都為對象類型。這樣就不會發(fā)生自動拆箱操作,以上代碼得到的b的結(jié)果為null。
PS:本文中的示例,只是為了更加方便讀者理解三目運(yùn)算符會導(dǎo)致自動拆箱現(xiàn)象,可能在代碼中并不會直接這樣使用。但是,我自己的代碼確實(shí)發(fā)生過類似問題。這里簡化一下,為了講清楚原理。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/69350.html
摘要:如果條件為,則邏輯非運(yùn)算符將得到進(jìn)行邏輯判斷的順序?yàn)閺淖蟮接?。再次檢測布爾表達(dá)式。其作用域限定在循環(huán)語句塊,其值與此時(shí)數(shù)組元素的值相等。 運(yùn)算符和邏輯控制 運(yùn)算符 java中的運(yùn)算符可以分為以下幾種: 算符運(yùn)算符 關(guān)系運(yùn)算符 位運(yùn)算符 邏輯運(yùn)算符 賦值運(yùn)算符 三目運(yùn)算符 算符運(yùn)算符 操作符 描述 + 加法 - 減法 * 乘法 / 除法 % 取余(模) ...
摘要:每日算法題目將碼位于之間的個(gè)字符顯示在屏幕上,為了美觀,要求小于的碼值前填充一個(gè),每打印個(gè)字符后換行。本文已轉(zhuǎn)載個(gè)人技術(shù)公眾號歡迎留言討論與點(diǎn)贊上一篇推薦貓說每日算法實(shí)現(xiàn)特殊累加值下一篇推薦貓說每日算法枚舉求就業(yè)率問題 Java每日算法 題目 將ASCII碼位于32-126之間的95個(gè)字符顯示在屏幕上,為了美觀,要求小于100的碼值前填充一個(gè)0,每打印8個(gè)字符后換行。 分析 這里先注意...
摘要:不要使用類函數(shù)終于,你不用再看到建議不要使用函數(shù)的提示了。因?yàn)閺暮诵纳贤耆瞥怂鼈?,這意味著請你移步至更好的類函數(shù),或者更靈活的層。將從數(shù)據(jù)庫獲取一個(gè)元數(shù)據(jù),如果您正在循環(huán)訪問特定文章的元數(shù)據(jù),則可以在循環(huán)中使用它。 showImg(https://segmentfault.com/img/bV75FM?w=1024&h=534); 1. 不要使用 mysql_ 類函數(shù) 終于,你不用...
摘要:強(qiáng)制類型轉(zhuǎn)換下標(biāo)運(yùn)算符變量與常量常量是在程序中的不會變化的數(shù)據(jù)變量其實(shí)就是內(nèi)存中的一個(gè)存儲空間,用于存儲數(shù)據(jù)。表示結(jié)束本次循環(huán),繼續(xù)下次循環(huán)。 Java知識點(diǎn)總結(jié) (基本語法) @(Java知識點(diǎn)總結(jié))[Java, Java基本語法] @(Java開發(fā))[Java基本語法] [toc] Java特點(diǎn) 簡單自然平臺可移植性支持函數(shù)式編程JIT 編譯更好的并發(fā)編程健壯安全 執(zhí)行方式 編譯...
摘要:注意,三目運(yùn)算符中和是成對出現(xiàn)的,最起碼數(shù)量上,有幾個(gè)就會有幾個(gè) 三目運(yùn)算符相信大家都很熟悉了: foo ? foo == true : foo == false 而三目運(yùn)算符?:?:?.....的調(diào)用方式大家也不陌生, 就相當(dāng)于一堆if - else if語句: foo ? foo == true : bar ? bar == true : bar == false 但是在zep...
閱讀 1983·2023-04-25 15:45
閱讀 1223·2021-09-29 09:34
閱讀 2510·2021-09-03 10:30
閱讀 2017·2019-08-30 15:56
閱讀 1473·2019-08-29 15:31
閱讀 1278·2019-08-29 15:29
閱讀 3209·2019-08-29 11:24
閱讀 3066·2019-08-26 13:45