問(wèn)題描述
今天有個(gè)同事發(fā)過(guò)來(lái)一個(gè)消息,說(shuō)他的系統(tǒng)今天老是報(bào)錯(cuò)誤,代碼看了好幾次都沒(méi)發(fā)現(xiàn)問(wèn)題,百思不得其解,讓我?guī)兔囱?。我拿到代碼后,乍一看也沒(méi)看出個(gè)所以然,但是在執(zhí)行單元測(cè)試之后,問(wèn)題很快就暴露出來(lái)了,坑啊~~~~ 大致代碼如下:
class Operator{
Long cityIdCharge;
.....
}
......
Operator operator = new Operator();
Long currentCityId = operator == null ? 0L : operator.getCityInCharge();
System.out.println(currentCityId);
在執(zhí)行如上代碼時(shí),將會(huì)拋出NPE異常,而且錯(cuò)誤位于代碼第二行。那為什么執(zhí)行第二行代碼會(huì)出現(xiàn)NPE錯(cuò)誤呢,下面我們來(lái)分析一下。
原因分析
根據(jù)異常堆棧,可以看到,異常定位于第二行代碼。第二行代碼是一個(gè)三元表達(dá)式,我們知道三目運(yùn)算符只要后面兩個(gè)是不同的類型,涉及到類型轉(zhuǎn)換,那么編譯器就會(huì)向下(基本類型)進(jìn)行轉(zhuǎn)型,再進(jìn)行計(jì)算。換言之,如果在計(jì)算表達(dá)式中,有Integer和int,那么Integer類型會(huì)先轉(zhuǎn)化為int再進(jìn)行計(jì)算。
在開(kāi)始分析代碼之前,需要回顧一下java的基本數(shù)據(jù)類型。long是8種基本數(shù)據(jù)類型之一,Long是long的包裝類,繼承于Number類。
在示例代碼中,operator.getCityInCharge()返回的是Long類型,值為null。三元表達(dá)式的第二個(gè)值是0L,我們知道,Long的默認(rèn)值是null.long的默認(rèn)值是0L,因此,對(duì)于上述示例代碼中的第二行代碼,Long和long不是同一個(gè)類型,編譯器會(huì)默認(rèn)向下轉(zhuǎn)型,Long轉(zhuǎn)為long,但是此時(shí)Long類型對(duì)應(yīng)的值為null,因此拋出NPE。
Long與long之間的相互轉(zhuǎn)化
- 拆箱操作
可以使用longValue()方法將Long變?yōu)閘ong
Long a = 1L;
long b = a.longValue();
也可以直接進(jìn)行拆箱操作
long c = a;
- 裝箱操作
可以使用new方法將long變成Long
long a = 1L;
Long b = new Long(a);
也可以直接進(jìn)行裝箱操作
Long c = a;
因此上述的示例代碼可以修改為
Long currentCityId = operator == null ? new Long(0) : operator.getCityInCharge();
或者使用Optional結(jié)構(gòu)對(duì)operator.getCityInCharge()進(jìn)行封裝后再進(jìn)行計(jì)算
Optional.ofNullable(operator.getCityInCharge()).orElse(0L)
寫(xiě)在最后
- 在使用對(duì)象時(shí),需要先驗(yàn)空,再進(jìn)行操作處理,也可以使用guava中的Optional對(duì)對(duì)象進(jìn)行封裝
- 使用三元運(yùn)算符時(shí)最后使用相同類型的對(duì)象
- 一般來(lái)說(shuō),屬性的值建議使用封裝類型,臨時(shí)變量建議使用基本類型