专业的JAVA编程教程与资源

网站首页 > java教程 正文

阿里 Java 开发手册提到的三目运算符的空指针问题是个怎么回事?

temp10 2024-09-27 02:48:37 java教程 12 ℃ 0 评论

手册内容

基础回顾

先简单介绍下本文要涉及到的几个重要概念,分别是”三目运算符”、”自动拆装箱”

三目运算符

在《The Java Language Specification》 中, 三 目 运 算 符 的 官 方 名 称 是 Conditional Operator ? : ,

阿里 Java 开发手册提到的三目运算符的空指针问题是个怎么回事?

形式为: < 表达式 1> ? < 表达式 2> : < 表达式 3>

以上,通过?、:组合的形式得到一个条件表达式。其中?运算符的含义是:先求表达式 1 的值,如果为真,则执行并返回表达式 2 的结果;如果表达式 1 的值为假,则执行并返回表达式 3 的结果。

值得注意的是,一个条件表达式从不会既计算 < 表达式 2>,又计算 < 表达式3>。条件运算符是右结合的,也就是说,从右向左分组计算。例如,a?b:c?d:e 将按 a?b:(c?d:e)执行。

自动装箱与自动拆箱

介绍过了三目运算符(条件表达式)之后,我们再来简单介绍下 Java 中的自动拆装箱相关知识点。

每一个 Java 开发者一定都对 Java 中的基本数据类型不陌生,Java 中共有 8 种基本数据类型,这些基础数据类型带来一个好处就是他们直接在栈内存中存储,不会在堆上分配内存,使用起来更加高效。

但是,Java 语言是一个面向对象的语言,而基本数据类型不是对象,导致在实际使用过程中有诸多不便,如集合类要求其内部元素必须是 Object 类型,基本数据类型就无法使用。

所以,相对应的,Java 提供了 8 种包装类型,更加方便在需要对象的地方使用。有了基本数据类型和包装类,带来了一个麻烦就是需要在他们之间进行转换。在 Java SE5 中,为了减少开发人员的工作,Java 提供了自动拆箱与自动装箱功能。

自动装箱 : 就是将基本数据类型自动转换成对应的包装类。

自动拆箱:就是将包装类自动转换成对应的基本数据类型。

Bash
 Integer i =10; // 自动装箱 
 int b= i; // 自动拆箱 

我们可以简单理解为,当我们自己写的代码符合装(拆)箱规范的时候,编译器 就会自动帮我们拆(装)箱。 自动装箱都是通过包装类的 valueOf() 方法来实现的 . 自动拆箱都是通过包装类对象的 xxxValue() 来实现的(如 booleanValue()、longValue() 等)。

问题演示

在最新版的开发手册中给出了一个例子,提示我们在使用三目运算符的过程中,可能会进行自动拆箱而导致 NPE 问题。

原文中的例子相对复杂一些,因为他还涉及到多个 Integer 相乘的结果是 int 的问题,我们举一个相对简单的一点的例子先来重现下这个问题:

Bash
 boolean flag = true; // 设置成 true,保证条件表达式的表达式二一定可以执行
 boolean simpleBoolean = false; // 定义一个基本数据类型的 boolean 变量
 Boolean nullBoolean = null;// 定义一个包装类对象类型的 Boolean 变量,值为 null
 boolean x = flag ? nullBoolean : simpleBoolean; // 使用三目运算符并给 x 变量赋值

以上代码,在运行过程中,会抛出 NPE:

 Exception in thread "main" java.lang.NullPointerException

而且,这个和你使用的 JDK 版本是无关的,分别在 JDK 6、JDK 8 和 JDK 14 上做了测试,均会抛出 NPE。

为了一探究竟,我们尝试对以上代码进行反编译,使用 jad 工具进行反编译后,得到以下代码

boolean flag = true;
boolean simpleBoolean = false;
Boolean nullBoolean = null;
boolean x = flag ? nullBoolean.booleanValue() : simpleBoolean;

可以看到,反编译后的代码的最后一行,编译器帮我们做了一次自动拆箱,而就是因为这次自动拆箱,导致代码出现对于一个 null 对象(nullBoolean.booleanValue())的调用,导致了 NPE。

那么,为什么编译器会进行自动拆箱呢?什么情况下需要进行自动拆箱呢?

原理分析

关于为什么编辑器会在代码编译阶段对于三目运算符中的表达式进行自动拆箱 ?

简单的来说就是:当第二位和第三位操作数的类型相同时,则三目运算符表达式的结果和这两位操作数的类型相同。当第二,第三位操作数分别为基本类型和该基本类型对应的包装类型时,那么该表达式的结果的类型要求是基本类型。

为了满足以上规定,又避免程序员过度感知这个规则,所以在编译过程中编译器如果发现三目操作符的第二位和第三位操作数的类型分别是基本数据类型 ( 如 boolean) 以及该基本类型对应的包装类型(如 Boolean)时,并且需要返回表达式为 包装类型,那么就需要对该包装类进行自动拆箱。

简单总结下,就是:当第二位和第三位表达式都是包装类型的时候,该表达式的结果才是该包装类型,否则,只要有一个表达式的类型是基本数据类型,则表达式得到的结果都是基本数据类型。如果结果不符合预期,那么编译器就会进行自动拆箱。

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表