美文网首页
三目运算符自动拆箱NPE分析

三目运算符自动拆箱NPE分析

作者: 但时间也偷换概念 | 来源:发表于2019-11-22 21:57 被阅读0次

背景

在笔者工作中曾经遇到过一次NPE异常,导致此异常的原因查到最后发现是三目运算符自动装箱导致的,我们来分析一下这个case。

首先我们来还原一下当时大致是怎样一个case。

  public class NpeTest {

    private Boolean bool;

    public Boolean getBool() {
        return bool;
    }

    public void setBool(Boolean bool) {
        this.bool = bool;
    }

    public static void main(String[] args) {
        NpeTest npeTest = new NpeTest();
        npeTest.setBool(null);

        Boolean test = npeTest != null ? npeTest.getBool() : false;
        System.out.println(test);
    }


}

如上述代码,当时大概是写了一个类似的三目运算符逻辑,然后在这里发生了NPE空指针异常。

分析

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.

我们来反编译最上面的代码看一下。
首先在iterm终端进入目标类路径,输入以下命令编译与反编译NpeTest类。

javac NpeTest.java 
javap -c NpeTest

最后输出信息如下:

public class org.springside.modules.utils.NpeTest {
  public org.springside.modules.utils.NpeTest();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  public java.lang.Boolean getBool();
    Code:
       0: aload_0
       1: getfield      #2                  // Field bool:Ljava/lang/Boolean;
       4: areturn

  public void setBool(java.lang.Boolean);
    Code:
       0: aload_0
       1: aload_1
       2: putfield      #2                  // Field bool:Ljava/lang/Boolean;
       5: return

  public static void main(java.lang.String[]);
    Code:
       0: new           #3                  // class org/springside/modules/utils/NpeTest
       3: dup
       4: invokespecial #4                  // Method "<init>":()V
       7: astore_1
       8: aload_1
       9: aconst_null
      10: invokevirtual #5                  // Method setBool:(Ljava/lang/Boolean;)V
      13: aload_1
      14: ifnull        27
      17: aload_1
      18: invokevirtual #6                  // Method getBool:()Ljava/lang/Boolean;
      21: invokevirtual #7                  // Method java/lang/Boolean.booleanValue:()Z
      24: goto          28
      27: iconst_0
      28: invokestatic  #8                  // Method java/lang/Boolean.valueOf:(Z)Ljava/lang/Boolean;
      31: astore_2
      32: getstatic     #9                  // Field java/lang/System.out:Ljava/io/PrintStream;
      35: aload_2
      36: invokevirtual #10                 // Method java/io/PrintStream.println:(Ljava/lang/Object;)V
      39: return
}

可以看到21后面跟的是booleanValue
所以基本可以确定是一个拆箱操作了
于是我们可以确定是因为三目运算符中,第二位是Boolean包装类型,第三位是boolean基本数据类型,所以进行了拆箱。
那么我们改一下再来看一下

public class NpeTest {

    private Boolean bool;

    public Boolean getBool() {
        return bool;
    }

    public void setBool(Boolean bool) {
        this.bool = bool;
    }

    public static void main(String[] args) {
        NpeTest npeTest = new NpeTest();
        npeTest.setBool(null);

        Boolean test = npeTest != null ? npeTest.getBool() : Boolean.FALSE;
        System.out.println(test);
    }


}

重新执行一下javac和javap

public class org.springside.modules.utils.NpeTest {
  public org.springside.modules.utils.NpeTest();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  public java.lang.Boolean getBool();
    Code:
       0: aload_0
       1: getfield      #2                  // Field bool:Ljava/lang/Boolean;
       4: areturn

  public void setBool(java.lang.Boolean);
    Code:
       0: aload_0
       1: aload_1
       2: putfield      #2                  // Field bool:Ljava/lang/Boolean;
       5: return

  public static void main(java.lang.String[]);
    Code:
       0: new           #3                  // class org/springside/modules/utils/NpeTest
       3: dup
       4: invokespecial #4                  // Method "<init>":()V
       7: astore_1
       8: aload_1
       9: aconst_null
      10: invokevirtual #5                  // Method setBool:(Ljava/lang/Boolean;)V
      13: aload_1
      14: ifnull        24
      17: aload_1
      18: invokevirtual #6                  // Method getBool:()Ljava/lang/Boolean;
      21: goto          27
      24: getstatic     #7                  // Field java/lang/Boolean.FALSE:Ljava/lang/Boolean;
      27: astore_2
      28: getstatic     #8                  // Field java/lang/System.out:Ljava/io/PrintStream;
      31: aload_2
      32: invokevirtual #9                  // Method java/io/PrintStream.println:(Ljava/lang/Object;)V
      35: return
}

可以看到booleanValue没有了,问题解决。

相关文章

  • 三目运算符自动拆箱NPE分析

    背景 在笔者工作中曾经遇到过一次NPE异常,导致此异常的原因查到最后发现是三目运算符自动装箱导致的,我们来分析一下...

  • 关于三目表达式问题

    记录一个使用三目表达式时遇到的问题:问题是由三目表达式与自动拆箱同时使用造成的(显然自动拆箱并不是我自己主动调用的...

  • NPE相关——Null Point Exception

    18.7.231、自动拆箱有可能产生NPE。类的Integer属性没有set值,直接在其他函数调用get方法,返回...

  • 装箱与拆箱详解笔记

    1、什么是自动装箱与拆箱 //自动装箱Integer integer = 100;//自动拆箱int i = in...

  • 暗藏杀机? 不简单的三目运算符号!

    最近,在一个业务改造中,使用三目运算符重构了业务代码,没想到测试的时候竟然发生 NPE (Null Pointer...

  • jdk5新特性

    自动装箱与拆箱 // 自动装箱:值转对象Integer n = 1;// 自动拆箱:对象转值int m = n; ...

  • Java自动装箱和拆箱,包装类缓存机制和JVM调节

    关于Java自动装箱和拆箱 基本数据(Primitive)类型的自动装箱(autoboxing)、拆箱(unbox...

  • (超详细)Java自动装箱拆箱

    详解自动拆箱与自动装箱 一、 什么是自动装箱、自动拆箱 简单一点说,装箱就是自动将基本数据类型转换为包装器类型,拆...

  • Java的自动装箱&&拆箱

    什么叫自动装箱和拆箱 简单来说: 自动装箱:就是自动将基础类型转换为包装器类型自动拆箱:就是自动将包装器类型转换为...

  • JAVA面试50讲之4:int和Integer的区别

    1.关于int和Integer的问题区别分析 1.1 编译阶段、运行时,自动装箱 / 自动拆箱是发生在什么阶段? ...

网友评论

      本文标题:三目运算符自动拆箱NPE分析

      本文链接:https://www.haomeiwen.com/subject/yqccwctx.html