Java三目运算符导致 NPE

news/2023/6/6 5:19:58

在三目运算符中,表达式 1 和 2 在涉及算术计算或数据类型转换时,会触发自动拆箱。当其中的操作数为 null 值时,会导致 NPE 。

一、基础知识

三目运算符

三目运算符是 Java 语言中的重要组成部分,它也是唯一有 3 个操作数的运算符。形式为:

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

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

自动装箱与自动拆箱

Java自动拆箱空指针异常

二、问题重现

如下代码在执行是会抛出空指针异常

        Double a = null;Integer b = null;Double c = Objects.nonNull(b) ? b : a;

异常:

三、原理

以上代码反编译出字节码如下:

 public static void main(java.lang.String[]);descriptor: ([Ljava/lang/String;)Vflags: ACC_PUBLIC, ACC_STATICCode:stack=2, locals=4, args_size=10: aconst_null1: astore_12: aconst_null3: astore_24: aload_25: invokestatic  #2                  // Method java/util/Objects.nonNull:(Ljava/lang/Object;)Z8: ifeq          1911: aload_212: invokevirtual #3                  // Method java/lang/Integer.intValue:()I15: i2d16: goto          2319: aload_120: invokevirtual #4                  // Method java/lang/Double.doubleValue:()D23: invokestatic  #5                  // Method java/lang/Double.valueOf:(D)Ljava/lang/Double;26: astore_327: return

  #12行可以看出b Integer类型变量开箱为int值,#20行可以看出a Double变量开箱为double值,#23行可以看出将根据条件,得出的double/int值装箱为Double类型的对象。翻译成java代码如下:

 Double c = Double.valueOf(Objects.nonNull(b) ? b.intValue() : a.doubleValue());

b,a都是null值,因此调用函数时报出npe异常。

分析之后我们可以得出这样的结论:三目运算符和自动拆箱导致了空指针异常。

那么,为什么编译器会进行自动拆箱呢?什么情况下需要进行自动拆箱呢?这其实是三目运算符的语法规范。参见jls-15.25,摘要如下:

 

以去翻阅一下。

其实简单总结下,就是:

1)如果第二个和第三个操作数具有相同的类型(可能是null类型),那么这就是条件表达式的类型。

2)如果第二个和第三个操作数中的一个是基本类型T,而另一个操作数的类型是对T应用装箱转换(§5.1.7)的结果,则条件表达式的类型为T。

3)如果第二个和第三个操作数中的一个是空类型,而另一个操作数的类型是引用类型,则条件表达式的类型就是该引用类型。

否则,如果第二个和第三个操作数的类型可转换为数值类型(§5.1.8),则有以下几种情况:

1)如果一个操作数是byte或Byte类型,另一个是short或Short类型,则条件表达式的类型为short。

2)如果其中一个操作数是T类型,其中T是byte、short或char,而另一个操作数是int类型的常量表达式(§15.28),其值可以用T类型表示,则条件表达式的类型为T。

3)如果其中一个操作数是T类型,其中T是字节、短或字符,而另一个操作数是int类型的常量表达式(§15.28),其值可以用U类型表示,U是对T应用开箱转换的结果,那么条件表达式的类型是U。

否则,二进制数值提升(§5.6.2)应用于操作数类型,条件表达式的类型是第二个和第三个操作数的提升类型。注意,二进制数字提升执行值集转换(§5.1.13),并可能执行开箱转换(§5.1.8)。

值集转换规则:

当操作符对一对操作数应用二进制数字提升,每个操作数必须表示一个可转换为数字类型的值时,应用以下规则,顺序:

1)如果任何操作数是引用类型,它将进行开箱转换(§5.1.8)。

2)扩展基元转换(§5.1.2)应用于按以下规则指定的任意一个或两个操作数转换:

  • 如果其中一个操作数为double类型,则另一个操作数转换为double类型。
  • 否则,如果其中一个操作数为float类型,则另一个操作数转换为float类型。
  • 否则,如果其中一个操作数为long类型,则另一个操作数转换为long类型。
  • 否则,两个操作数都转换为int类型。

我自己的话总结一下,npe其实是值集转换引起的,当以下操作符不能自然转换时(比如一个值为byte,一个值为short,可以),会产生值集转换,而根据值集转换规则1),任何引用类型都要开箱,那么空值开箱就会导致NPE。

最根的解决方法就是阿里巴巴新出的规范:

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.exyb.cn/news/show-4571342.html

如若内容造成侵权/违法违规/事实不符,请联系郑州代理记账网进行投诉反馈,一经查实,立即删除!

相关文章

《程序是调试出来的》嵌入式Keil5的调试技巧

程序是调试出来的Keil5的调试技巧1.注释//2.既然板子在手&#xff0c;不如软硬结合3.善用调试小工具4.休息一下&#xff0c;出去走走&#xff0c;理清程序思路Keil5的调试技巧 调试手段是教不会的&#xff0c;而是自己亲身实践出来的。那笔者撰写这篇文章的意义何在&#xff1f…

KEIL5MDK最新版(3.37)安装以及旧编译器(V5)安装

最近KEIl5最新版本出来了&#xff0c;但官方不在默认安装V5编译器&#xff0c;导致某些代码无法兼容&#xff0c;为了防止搞忘&#xff0c;便把方法上传网上。旧编译器的安装思路是:在以前有V5编译器的KEILMDK安装包中复制粘贴到新的KEIL5中。为了节约下载时间&#xff0c;笔者…

【Keil】Keil5无法更改背景色和字体解决方案

Keil5不知道抽什么风一直改不了背景色和字体&#xff0c;干脆就全局设置文档来改变字体和背景色吧… 1 先找到global.prop文件 最好使用notepad或者其他有替换功能的编辑器打开&#xff0c;不推荐记事本… 2 找到需要修改的地方 这是cpp文件编写环境的外观设置&#xff0c;…

SQL注入之联合查询注入与报错注入

数据来源 本文仅用于信息安全的学习&#xff0c;请遵守相关法律法规&#xff0c;严禁用于非法途径。若观众因此作出任何危害网络安全的行为&#xff0c;后果自负&#xff0c;与本人无关。 SQL注入之联合查询 sql注入简单演示 1. 判断sq注入 2. 闭合然后爆列 3. 查看显示列 …

keil5的安装与破解

下载这几个文件 1. 点击MDK518 2. 点击MDKCM518&#xff0c;这是扩展包 3. 点击压缩包里面的 4. 打开keil 5. File 6. 复制 输入到 7. 选择 8. 复制 粘贴到 &#xff0c;点击ADD LIC 9. 此时出现 10. 在复制 &#xff0c;重复 11. 注册成功

考研复习408计算机网络——物理层

物理层通信相关内容奈奎斯特定理&#xff0c;香农定理编码与调制数据交换方式传输介质物理层设备物理层接口的特性中继器集线器hub通信相关内容 这部分根据我的复习&#xff0c;感觉和通信的内容紧密联系&#xff0c;而且还是刚开始的内容&#xff0c;容易劝退很多人。实际上把…

让渡隐私权的囚徒

转自&#xff1a;http://wenzhang.ztcztc.com/Detail.aspx?idA2E06936-1EDF-B06C-E11D-51CF2B6BA6AB 隐私权代表着不被他人打扰的权利&#xff0c;即每个人都有对自己的隐私是否向他人公开的决定权。但由于隐私权在我国法律上还未完善&#xff0c;所以导致了一些公众人物成为了…

隐私权政策说明书

生效日期&#xff1a;2022年 5 月 18 日 您的信任对我们非常重要&#xff0c;我们深知个人信息对您&#xff08;以下亦称“用户”&#xff09;的重要性&#xff0c;我们将按法律法规要求&#xff0c;采取相应安全保护措施&#xff0c;尽力保护您的个人信息安全可控。在您开始使…