△Hollis, 一个对Coding有着独特追求的人△

这是Hollis的第 267篇原创分享

作者 l Hollis

来源 l Hollis(ID:hollischuang)

最近,阿里巴巴Java开发手册发布了最新版,泰山版,这个名字起的不错,一览众山小。

新版据说新增了30+规约,我还没来得及仔细去看,不过有粉丝和我说,其中新增的一条规约,他之前在我的博客中看到过。

仔细看了下,这个问题确实我很久之前遇到过,确实曾经在博客中也记录过。

最初遇到这个问题的是我的同事,他在代码中使用了三目运算符,代码在线上运行的时候发生了NPE,经过排查,发现原来是三目运算符和自动拆装箱之间有一定的关系,导致了空指针。

这篇文章最开始发布于2015年,目前已经有1w+阅读量了。

趁着最新的开发手册中也提到了这个点,于是把之前的文章内容翻出来并重新整理了一下,带大家一起回顾下这个知识点。

一、三目运算符

对于条件表达式b?x:y,先计算条件b,然后进行判断。如果b的值为true,计算x的值,运算结果为x的值;否则,计算y的值,运算结果为y的值。一个条件表达式从不会既计算x,又计算y。条件运算符是右结合的,也就是说,从右向左分组计算。例如,a?b:c?d:e将按a?b:(c?d:e)执行。

二、自动装箱与自动拆箱

基本数据类型的自动装箱(autoboxing)、拆箱(unboxing)是自J2SE 5.0开始提供的功能。

一般我们要创建一个类的对象实例的时候,我们会这样:Class a = new Class(parameters);

当我们创建一个Integer对象时,却可以这样:Integer i = 100;(注意:和 int i = 100;是有区别的 )

实际上,执行上面那句代码的时候,系统为我们执行了:Integer i = Integer.valueOf(100);

这里暂且不讨论这个原理是怎么实现的(何时拆箱、何时装箱),也略过普通数据类型和对象类型的区别。

我们可以理解为,当我们自己写的代码符合装(拆)箱规范的时候,编译器就会自动帮我们拆(装)箱。

那么,这种不被程序员控制的自动拆(装)箱会不会存在什么问题呢?

三、问题回顾

首先,通过你已有的经验看一下下面这段代码:

Map<String,Boolean> map =  new HashMap<String, Boolean>();Boolean b = (map!=null ? map.get("test") : false);

以上这段代码,是我们在不注意的情况下有可能经常会写的一类代码(在很多时候我们都爱使用三目运算符)。当然,这段代码是存在问题的,执行该代码,会报NPE.

Exception in thread "main" java.lang.NullPointerException

首先可以明确的是,既然报了空指针,那么一定是有些地方调用了一个null的对象的某些方法。

在这短短的两行代码中,看上去只有一处方法调用map.get("test"),但是我们也都是知道,map已经事先初始化过了,不会是Null,那么到底是哪里有空指针呢。

我们接下来反编译一下该代码。看看我们写的代码在经过编译器处理之后变成了什么样。

反编译后代码如下:

HashMap hashmap = new HashMap();Boolean boolean1 = Boolean.valueOf(hashmap == null ? false : ((Boolean)hashmap.get("test")).booleanValue());

看完这段反编译之后的代码之后,经过分析我们大概可以知道问题出在哪里。

((Boolean)hashmap.get("test")).booleanValue()的执行过程及结果如下:

hashmap.get("test")->null;

(Boolean)null->null;

null.booleanValue()->报错

好,问题终于定位到了。那么接下来看看如何解决该问题以及为什么会出现这种问题。

四、原理分析

通过查看反编译之后的代码,我们准确的定位到了问题,分析之后我们可以得出这样的结论:NPE的原因应该是三目运算符和自动拆箱导致了空指针异常。

根据规定,三目运算符的第二、第三位操作数的返回值类型应该是一样的,这样才能当把一个三目运算符的结果赋值给一个变量。

如:Person i = a>b ? i1:i2; ,就要求i1和i2的类型都必须是Person才行。

因为Java中存在一种特殊的情况,那就是基本数据类型和包装数据类型可以通过自动拆装箱的方式互相转换。即可以定义int i = new Integer(10);也可以定义Integer i= 10;

那如果,三目运算符的第二位和第三位的操作数的类型分别是基本数据类型和包装类型对象时,就需要有一方需要进行自动拆装箱。

那到底如何做的呢,根据三目运算符的语法规范。参见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.

简单的来说就是:当第二,第三位操作数分别为基本类型和对象时,其中的对象就会拆箱为基本类型进行操作。

所以,结果就是:由于使用了三目运算符,并且第二、第三位操作数分别是基本类型和对象。所以对对象进行拆箱操作,由于该对象为null,所以在拆箱过程中调用null.booleanValue()的时候就报了NPE。

五、问题解决

如果代码这么写,就不会报错:

Map<String,Boolean> map =  new HashMap<String, Boolean>();Boolean b = (map!=null ? map.get("test") : Boolean.FALSE);

就是保证了三目运算符的第二第三位操作数都为对象类型。

这和三目运算符有关。

关于作者Hollis,一个对Coding有着独特追求的人,现任阿里巴巴技术专家,个人技术博主,技术文章全网阅读量数千万,《程序员的三门课》联合作者。


Java工程师成神之路系列文章在 GitHub 更新中,欢迎关注,欢迎star。 直面Java第313期:运行时常量池在JDK各个版本中的实现?深入并发第013期:拓展synchronized——锁优化
- MORE | 更多精彩文章 -你们要的《Java工程师成神之路》高清版思维导图,来了!
漫话:为什么生僻字计算机上打不出来,或者打出来也无法显示呢?
太有才了,网友总结的罗志祥事件吃瓜文案!每一个都能笑抽
轻轻一扫,立刻扣款,付款码背后的原理你不想知道吗?
如果你喜欢本文,请长按二维码,关注 Hollis.转发至朋友圈,是对我最大的支持。好文章,我在看❤️

5年前我在博客中写的三目运算符的空指针问题,终于被阿里巴巴开发手册收录了。...相关推荐

  1. 我从写技术博客中收获到了什么?- J_Knight_

    我是 J_Knight_,熟悉我的人都应该知道我是一名刚工作满3年的非科班 iOS 开发者,而且一直坚持写技术博客快有2年半的时间了. 其实从去年开始就一直想分享我写博客的心得,但是一直也没有找到合适 ...

  2. 聊聊在博客园写博客的这两年《Unity 3D脚本编程:使用C#语言开发跨平台游戏》正式出版...

    版本状态: 2016.9 第一次印刷 (2016.11 输出到台湾) 2017.1 第二次印刷 2017.5 第三次印刷 2017.5 电子书上线:Unity 3D脚本编程--使用C#语言开发跨平台游 ...

  3. 在博客园写了一年博客,收获的不仅仅是写作技能——我能一直保持积极的学习和工作态度...

    转眼一算,我在博客园写博客已经有1年了.先用些数据来展示下这一年的成果. 1  目前粉丝414位. 2 排名3788,离3000名尚有一段距离,还需努力.3 最近每周一篇博文,虽然说有些博文里有鸡汤元 ...

  4. IT-标准化-系列-14.用-博客三剑客-写博客

    有人说写博客-说写吧! 那有博客三剑客 下面我来介绍: 剑客 1:红蜻蜓抓图精灵-截图利器 剑客 2:Windows Live Writer-写作利器 剑客 3:Batch Photo WaterMa ...

  5. 同步 GIT@OSC 实现MARKDOWN文件发布或更新到CSDN博客中

    还记得前面发过一篇文章,写的是关于从GITBLOG迁移博客内容到CSDN博客的文章传送门,这篇文章也是基于此写的 相信很多同学都有过这样的历程,自己辛辛苦苦写的博客,因为域名空间或等等各种原因丢失了的 ...

  6. CSDN博客中删除空白代码块

    在我们编写CSDN博客的时候,有时候我们创建了一些代码块然后删去时,会出现在博客中还有空白代码块出现的情况,比如我自己写的这个博客: 而我们在编辑模式修改时是看不到这个空白代码块的: 那我们应该如何删 ...

  7. hexo博客中插入图片失败——解决思路及个人最终解决办法

    文章目录 1.前言 2.解决方案 2.1 创建图片资源文件夹 2.2 typora中图像设置 2.3 插件下载 2.4 修改md图片路径 小结 1.前言 今天晚上花了大概1个小时在晚上搜寻各种hexo ...

  8. 我来告诉你,那些博客中的gif动态图是怎么弄的?

    大家都知道,图片比单纯的文字给人的视觉冲击力更大,表达的内容也更直观易懂,经常在博客上看到动态的gif图,那这些gif动态图是怎么制作的呢,特别是Android移动开发者,写了一个demo,写博客时, ...

  9. 我为什么要写博客,写博客的意义是什么??

    曾经何时我也不知道,怎样才能变成我自己所羡慕的大佬!!在一次次的CSDN阅读的过程中,结实了许多志同道合的人!!包过凉哥,擦姐--大佬,但是,很遗憾,与这些人只是单纯的任务(写文)之交,没有过多的深入 ...

最新文章

  1. Confluence 6 数据库表-系统信息(System information)
  2. 【线段树合并】解题报告:luogu P4556雨天的尾巴 (树上对点差分 + 动态开点 + 线段树合并)线段树合并模板离线/在线详解
  3. vector大小为1,如果直接输出它-2,为何不是-1?
  4. 存储时间:高清3D化,技术应用化?
  5. .NET Remoting构建分布式数据库查询
  6. OS_FLAG.C(1)
  7. linux命令crontab
  8. HashMap vs ConcurrentHashMap — 示例及Iterator探秘
  9. 使用inspector功能查看和管理契约
  10. python list中append()与extend()用法
  11. IS-IS详解(十三)——IS-IS 路由开销类型
  12. cad墙线打断lisp_CAD墙体如何打断,用tr命令不行,打散的话墙线会错位?
  13. Oracle SQL计算周
  14. 反垃圾邮件系统|基于Springboot+vue 实现反垃圾邮件系统
  15. 2020腾讯教育盛典,乐博乐博被评为“年度科技创新教育品牌”
  16. PCL学习:随机采样一致性算法(RANSAC)
  17. 西电研一人工智能复习随笔
  18. 完美世界16.65亿出售院线业务;共享床位涉黄被下线;宜家创始人去世丨价值早报
  19. 学生学籍管理系统jsp源代码 MySql_jsp学籍管理系统——mysql+java web
  20. 视频里的声音怎么转换成音频?

热门文章

  1. Ubuntu中apt与apt-get命令的区别
  2. docker 提交自定义镜像
  3. c语言编程顺序查找例题,C语言典型编程例题.doc
  4. mysql 安装1607_mysql服务启动报1607error
  5. vue中webpack默认配置_webpack中Entry与Output的基础配置
  6. 能力共进、价值共赢,华为传媒携手生态伙伴推动行业数字化转型和智慧化升级
  7. vb 通过php连接mysql数据库连接_vb如何mysql数据库连接
  8. 操作系统之内存管理:6、页面分配策略、抖动、工作集
  9. (王道408考研数据结构)第八章排序-第二节:直接插入排序和希尔排序
  10. USACO-Section2.3 Longest Prefix