Java三目运算符导致NPE-创新互联

在三目运算符中,表达式 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;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=2, locals=4, args_size=1
         0: aconst_null
         1: astore_1
         2: aconst_null
         3: astore_2
         4: aload_2
         5: invokestatic  #2                  // Method java/util/Objects.nonNull:(Ljava/lang/Object;)Z
         8: ifeq          19
        11: aload_2
        12: invokevirtual #3                  // Method java/lang/Integer.intValue:()I
        15: i2d
        16: goto          23
        19: aload_1
        20: invokevirtual #4                  // Method java/lang/Double.doubleValue:()D
        23: invokestatic  #5                  // Method java/lang/Double.valueOf:(D)Ljava/lang/Double;
        26: astore_3
        27: 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。

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

你是否还在寻找稳定的海外服务器提供商?创新互联www.cdcxhl.cn海外机房具备T级流量清洗系统配攻击溯源,准确流量调度确保服务器高可用性,企业级服务器适合批量采购,新人活动首月15元起,快前往官网查看详情吧


网站名称:Java三目运算符导致NPE-创新互联
URL地址:http://azwzsj.com/article/dcegoh.html