5年前我在博客中写的三目运算符的空指针问题,终于被阿里巴巴开发手册收录了。...
△Hollis, 一個(gè)對(duì)Coding有著獨(dú)特追求的人△
這是Hollis的第?267篇原創(chuàng)分享
作者 l Hollis
來源 l Hollis(ID:hollischuang)
最近,阿里巴巴Java開發(fā)手冊(cè)發(fā)布了最新版,泰山版,這個(gè)名字起的不錯(cuò),一覽眾山小。
新版據(jù)說新增了30+規(guī)約,我還沒來得及仔細(xì)去看,不過有粉絲和我說,其中新增的一條規(guī)約,他之前在我的博客中看到過。
仔細(xì)看了下,這個(gè)問題確實(shí)我很久之前遇到過,確實(shí)曾經(jīng)在博客中也記錄過。

最初遇到這個(gè)問題的是我的同事,他在代碼中使用了三目運(yùn)算符,代碼在線上運(yùn)行的時(shí)候發(fā)生了NPE,經(jīng)過排查,發(fā)現(xiàn)原來是三目運(yùn)算符和自動(dòng)拆裝箱之間有一定的關(guān)系,導(dǎo)致了空指針。
這篇文章最開始發(fā)布于2015年,目前已經(jīng)有1w+閱讀量了。
趁著最新的開發(fā)手冊(cè)中也提到了這個(gè)點(diǎn),于是把之前的文章內(nèi)容翻出來并重新整理了一下,帶大家一起回顧下這個(gè)知識(shí)點(diǎn)。
一、三目運(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é)合的,也就是說,從右向左分組計(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開始提供的功能。
一般我們要?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í)裝箱),也略過普通數(shù)據(jù)類型和對(duì)象類型的區(qū)別。
我們可以理解為,當(dāng)我們自己寫的代碼符合裝(拆)箱規(guī)范的時(shí)候,編譯器就會(huì)自動(dòng)幫我們拆(裝)箱。
那么,這種不被程序員控制的自動(dòng)拆(裝)箱會(huì)不會(huì)存在什么問題呢?
三、問題回顧
首先,通過你已有的經(jīng)驗(yàn)看一下下面這段代碼:
Map<String,Boolean>?map?=??new?HashMap<String,?Boolean>();Boolean?b?=?(map!=null???map.get("test")?:?false);以上這段代碼,是我們?cè)诓蛔⒁獾那闆r下有可能經(jīng)常會(huì)寫的一類代碼(在很多時(shí)候我們都愛使用三目運(yùn)算符)。當(dāng)然,這段代碼是存在問題的,執(zhí)行該代碼,會(huì)報(bào)NPE.
Exception in thread "main" java.lang.NullPointerException
首先可以明確的是,既然報(bào)了空指針,那么一定是有些地方調(diào)用了一個(gè)null的對(duì)象的某些方法。
在這短短的兩行代碼中,看上去只有一處方法調(diào)用map.get("test"),但是我們也都是知道,map已經(jīng)事先初始化過了,不會(huì)是Null,那么到底是哪里有空指針呢。
我們接下來反編譯一下該代碼。看看我們寫的代碼在經(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()->報(bào)錯(cuò)
好,問題終于定位到了。那么接下來看看如何解決該問題以及為什么會(huì)出現(xiàn)這種問題。
四、原理分析
通過查看反編譯之后的代碼,我們準(zhǔn)確的定位到了問題,分析之后我們可以得出這樣的結(jié)論:NPE的原因應(yīng)該是三目運(yùn)算符和自動(dòng)拆箱導(dǎo)致了空指針異常。
根據(jù)規(guī)定,三目運(yùn)算符的第二、第三位操作數(shù)的返回值類型應(yīng)該是一樣的,這樣才能當(dāng)把一個(gè)三目運(yùn)算符的結(jié)果賦值給一個(gè)變量。
如:Person i = a>b ? i1:i2; ,就要求i1和i2的類型都必須是Person才行。
因?yàn)镴ava中存在一種特殊的情況,那就是基本數(shù)據(jù)類型和包裝數(shù)據(jù)類型可以通過自動(dòng)拆裝箱的方式互相轉(zhuǎn)換。即可以定義int i = new Integer(10);也可以定義Integer i= 10;
那如果,三目運(yùn)算符的第二位和第三位的操作數(shù)的類型分別是基本數(shù)據(jù)類型和包裝類型對(duì)象時(shí),就需要有一方需要進(jìn)行自動(dòng)拆裝箱。
那到底如何做的呢,根據(jù)三目運(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ù)诙?#xff0c;第三位操作數(shù)分別為基本類型和對(duì)象時(shí),其中的對(duì)象就會(huì)拆箱為基本類型進(jìn)行操作。
所以,結(jié)果就是:由于使用了三目運(yùn)算符,并且第二、第三位操作數(shù)分別是基本類型和對(duì)象。所以對(duì)對(duì)象進(jìn)行拆箱操作,由于該對(duì)象為null,所以在拆箱過程中調(diào)用null.booleanValue()的時(shí)候就報(bào)了NPE。
五、問題解決
如果代碼這么寫,就不會(huì)報(bào)錯(cuò):
Map<String,Boolean>?map?=??new?HashMap<String,?Boolean>();Boolean?b?=?(map!=null???map.get("test")?:?Boolean.FALSE);就是保證了三目運(yùn)算符的第二第三位操作數(shù)都為對(duì)象類型。
這和三目運(yùn)算符有關(guān)。
關(guān)于作者:Hollis,一個(gè)對(duì)Coding有著獨(dú)特追求的人,現(xiàn)任阿里巴巴技術(shù)專家,個(gè)人技術(shù)博主,技術(shù)文章全網(wǎng)閱讀量數(shù)千萬,《程序員的三門課》聯(lián)合作者。
Java工程師成神之路系列文章在 GitHub 更新中,歡迎關(guān)注,歡迎star。?直面Java第313期:運(yùn)行時(shí)常量池在JDK各個(gè)版本中的實(shí)現(xiàn)?深入并發(fā)第013期:拓展synchronized——鎖優(yōu)化 - MORE | 更多精彩文章 -你們要的《Java工程師成神之路》高清版思維導(dǎo)圖,來了! 漫話:為什么生僻字計(jì)算機(jī)上打不出來,或者打出來也無法顯示呢? 太有才了,網(wǎng)友總結(jié)的羅志祥事件吃瓜文案!每一個(gè)都能笑抽 輕輕一掃,立刻扣款,付款碼背后的原理你不想知道嗎? 如果你喜歡本文,請(qǐng)長按二維碼,關(guān)注?Hollis.轉(zhuǎn)發(fā)至朋友圈,是對(duì)我最大的支持。好文章,我在看??總結(jié)
以上是生活随笔為你收集整理的5年前我在博客中写的三目运算符的空指针问题,终于被阿里巴巴开发手册收录了。...的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Servlet服务器搭建过程中一些经验
- 下一篇: QT5.7下载安装