最近在开发中遇到一个莫名其妙的bug,一个给Double类型赋值的三段式报了空指针异常,从程序字面上完全看不出有什么问题。项目代码,不方便直接贴,我写了下面这段例子进行说明:
直接使用null赋值给三段式
1 | public static Double test() { |
这段代码能够成功执行,看一下编译后的字节码吧(通过ItelliJ查看反编译后的代码)
1 | public static Double test() { |
对于三段式中为double类型的可选项 b.doubleValue(),会将b.doubleValue()进行包装再赋值给result.由于另一个可选项是常量null,所以不会进行拆包。
使用三段式赋值,并且其中一个值是值为null的包装类对象
1 | public static Double test2() { |
给result赋值的那句报了空指针异常,这是很奇怪的事,因为按照正常理解,b为null时,直接给将temp赋值给result,result值变成null就好了。
直接看编译后的字节码吧
1 | public static Double test2() { |
因为三段式中,一个可选值为 b.doubleValue(),是原始的double类型,另一个可选项是变量。编译器就试图先取两个可选值的double值,再包装成Double类型,由于temp内容为null,报空指针异常了。实际上,如果把temp替换成一个返回值为Double类型的函数,比如temp(),也会进行相同的处理,如果temp()值为空,也会报空指针异常。另外,如果
b.doubleValue()改成double常量,比如1d,也是一样的。
使用if语句而不使用三段式对包装类赋值
1 | public static Double test3() { |
这段代码从语义上与三段式的那段代码是等价的,看看编译后的结果:
1 | public static Double test3() { |
两个条件分支互相不影响,所以,对于temp,由于不关心double值,所以,直接进行对象级别的赋值就好了,所以不会报空指针异常。
总结
在三段式中,如果一个可选项为double类型(无论变量还是常量),另一个可选项为值为null的Double类型变量或函数,就会因为拆包的问题报空指针异常。其它包装类型也是如此。平时在使用包装类,应当特别注意这一点。