问题描述
今天有个同事发过来一个消息,说他的系统今天老是报错误,代码看了好几次都没发现问题,百思不得其解,让我帮忙看眼。我拿到代码后,乍一看也没看出个所以然,但是在执行单元测试之后,问题很快就暴露出来了,坑啊~~~~ 大致代码如下:
class Operator{
Long cityIdCharge;
.....
}
......
Operator operator = new Operator();
Long currentCityId = operator == null ? 0L : operator.getCityInCharge();
System.out.println(currentCityId);
在执行如上代码时,将会抛出NPE异常,而且错误位于代码第二行。那为什么执行第二行代码会出现NPE错误呢,下面我们来分析一下。
原因分析
根据异常堆栈,可以看到,异常定位于第二行代码。第二行代码是一个三元表达式,我们知道三目运算符只要后面两个是不同的类型,涉及到类型转换,那么编译器就会向下(基本类型)进行转型,再进行计算。换言之,如果在计算表达式中,有Integer和int,那么Integer类型会先转化为int再进行计算。
在开始分析代码之前,需要回顾一下java的基本数据类型。long是8种基本数据类型之一,Long是long的包装类,继承于Number类。
在示例代码中,operator.getCityInCharge()返回的是Long类型,值为null。三元表达式的第二个值是0L,我们知道,Long的默认值是null.long的默认值是0L,因此,对于上述示例代码中的第二行代码,Long和long不是同一个类型,编译器会默认向下转型,Long转为long,但是此时Long类型对应的值为null,因此抛出NPE。
Long与long之间的相互转化
- 拆箱操作
可以使用longValue()方法将Long变为long
Long a = 1L;
long b = a.longValue();
也可以直接进行拆箱操作
long c = a;
- 装箱操作
可以使用new方法将long变成Long
long a = 1L;
Long b = new Long(a);
也可以直接进行装箱操作
Long c = a;
因此上述的示例代码可以修改为
Long currentCityId = operator == null ? new Long(0) : operator.getCityInCharge();
或者使用Optional结构对operator.getCityInCharge()进行封装后再进行计算
Optional.ofNullable(operator.getCityInCharge()).orElse(0L)
写在最后
- 在使用对象时,需要先验空,再进行操作处理,也可以使用guava中的Optional对对象进行封装
- 使用三元运算符时最后使用相同类型的对象
- 一般来说,属性的值建议使用封装类型,临时变量建议使用基本类型
网友评论