Java三元运算符与拆箱装箱引起的误会

场景

一次线上问题,出现了NPE,当时的代码类似如下:

1. Map<String, 业务对象> map = xxxService.findXxx(xxx);

2. Long result = map.get(xxx) != null ? map.get(xxx).getId() : 0L;

就是第二行出错了,正常来说,当map.get(xxx)时,咱已经判断了null了,只有非null才会get呀,怎么还会出现null异常呢?

排查问题的过程当中,搜到了这么一篇博客:https://blog.csdn.net/prestonzzh/article/details/52538541

大致的意思是说使用三元运算符的时候,CPU会为了速度更快,会将三元运算符两边的结果都计算出来,

这问题就大了,用了这么久的三元运算符也没出现过这种问题呀,搜索网络上也咩有人碰到过这种坑,并且java里的三元运算符也是生成字节码运行的,没有直接和CPU打交道,那咱去看看字节码是啥样子的吧:

public static void main(String[] args) {
    boolean condition = false;
    Long data = 2L;
    Long res = condition ? data : 0L;
    System.out.println(res);
 }

image.png

可以看到,三元运算符编译成字节码后,并不是三元运算符了,也是通过IFEQGOTO这种类似if else执行相应的分支的,

这说明因为CPU的优化导致的两边都执行出现的NPE错误站不住脚,程序肯定是先判断条件,再根据条件走分支的,

此处细心一点我们可以看见,在字节码中出现了Long.longValueLong.valueOf这种字眼,这里不卖关子了,

这涉及到java中的拆箱装箱概念,当基本类型和包装类型在一起时,包装类型会被自动转换成基本类型,也就是说,上面的data和0L在一起时,

data在字节码中会被自动变成data.longValue()变成基本类型,也就是拆箱,所以这时候是很危险的,如果data是null的话,岂不是会出错?我们验证一下:

image.png

可以看见,直接出现了NPE, 这说明包装类型和基本类型在一起的时候是有坑的,一不小心包装类型是null,在语法阶段没什么问题,但是到运行阶段出NPE的错误了,

这时候我们可以把0L变成包装类型,这时候java就不会自动帮我们变成基本类型了,因为两个都是包装类型就不会拆箱了,

image.png

顺利输出null,

可以看到字节码中现在已经没有Long.longValueLong.valueOf这种字眼了,说明没有被拆箱。

image.png

问题现象解决

顺着上面出现拆箱装箱问题,我怀疑是map.get(xxx)获取到的值不为null,但是getId()属性获取到的是null,所以出现了拆箱,导致了NPE,

1. Map<String, 业务对象> map = xxxService.findXxx(xxx);

2. Long result = map.get(xxx) != null ? map.get(xxx).getId() : 0L;

最后根据查看xxxService.findXxx(xxx)代码时发现,如果没有找到xxx相应的信息时,就new了一个业务对象返回回来,但是new出来的对象里的值都是null,所以出现了拆箱导致了NPE,

这个问题其实就是拆箱装箱导致的,远没有到CPU层面~ 日后开发过程中多注意细节,不能轻易怀疑CPU,哈哈~

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值