JDK自己主动拆箱下,三目运算符的潜规则
近期发现了一个非常诡异的NullPointerException,在以下这种方法抛出,一開始怎么都没想明确,dSrc即使为null,那直接赋值给distinct也没问题啊。
private Doubledistinct;
private void setParam(Double dSrc, boolean flag) {
this.distinct = (flag) ? dSrc : 0d;
}
最后才发现是Java自己主动拆箱的潜规则,以下我们来看看其所以然。
自己主动装箱/拆箱
在JDK1.5引入自己主动装箱/拆箱,提高了我们的开发效率,也让我们的代码变得更加简洁,不用显式转换:
Double dWrap1 = 10d;
double d1 = dWrap1;
double d2 = d1 + dWrap1;
DoubledWarp2 = d2 + dWrap1;
实际上,自己主动装箱/拆箱是通过编译器来支持的,JVM并没有改变。我们反编译能看到上面的源代码会变成:
Double dWrap1 = Double.valueOf(10.0d);
double d1 = dWrap1.doubleValue();
double d2 = d1 + dWrap1.doubleValue();
DoubledWarp2 = Double.valueOf(d2 + dWrap1.doubleValue());
编译器的意图非常明显,帮我们完毕基本类型和封装类型的相互转换;另外,对于封装类的运算中,先要转换成基本类型,再进行计算。
可是,这么自己主动转换,问题就来了,假设我把dWrap1初始化为null,再赋值给d1,相当于把null赋值给了基本类型double。编译的时候是没有问题的,由于编译器还觉得是封装类Double类型,会帮我们自己主动拆箱赋值给d1,仅仅是执行的时候会抛NullPointerException,例如以下:
Double dWarp1 = null;
double d1 = dWarp1;
这事实上是个非常低级的bug,非常easy防范,加个非空校验就能够避免。一般原则也是,在使用每一个Object之前都要做非空校验,一些代码检查工具也会帮我们做这个校验,如FindBugs。所以我们能够写成下面形式:
Double dWarp1 = null;
double d1 = 0d;
if (null != dWarp1) {
d1 = dWarp1;
}
三目运算的潜规则
有时候,我们为了代码的简洁性,会引入三目运算符:
double d1 = (null != dWarp1) ? dWarp1 : 0d;
可是,也有比較诡异的情况:依据条件flag推断,假设true则赋值dWarp1,否则设为默认值0,例如以下。
Double dWarp1 = null;
boolean flag =true;
DoubledWarp2 = (flag) ? dWarp1 : 0d;
这乍眼一看,非常正常嘛,相当于dWarp2 = dWarp1,可是执行起来却会抛异常NullPointerException。
这就是编译器的自己主动装箱/拆箱转换引起的问题。我们反编译就能看到,原来他对dWarp1做了一层拆箱,这样就出现前面我们所说的问题,假设dWarp1为null的话,就挂了。
DoubledWarp2 = Double.valueOf((flag) ? dWarp1.doubleValue() : 0.0D);
事实上,这是自己主动装箱/拆箱的特性,仅仅要一个运算中有不同的类型,涉及到类型转换,那么编译器会往下(基本类型)转型,再进行运算。
就是说,假设运算中有int和Integer,Integer会先转成int再计算。
所以以下这样的写法照样会抛出NullPointerException:
Double dWarp1 = null;
Long l2 = null;
boolean flag =false;
DoubledWarp2 = (flag) ? dWarp1 : l2;
由于dWarp1和l2都要先转换成基本类型,而不是互相转换,反编译后变成:
Double dWarp2 = Double.valueOf((flag)? dWarp1.doubleValue() : l2.longValue());
因此,这里能够改成以下三种方式:
1.在赋值前,先做非空校验,可是这样做比較繁琐,由于非常多时候dWarp2是能够接受null的,这个非空推断仅仅是用来避免编译器的自己主动拆箱异常。
2.避免使用三目运算符,只是预计会有非常多人不忍抛弃三目(我也算一个)。
Double dWarp1 = null;
boolean flag =true;
Double dWarp2 = 0d;
if (flag) {
dWarp2 = dWarp1;
}
3.统一运算中的类型,避免类型的混用了。(个人认为这样的比較优雅)
Double dWarp1 = null;
boolean flag =true;
DoubledWarp2 = (flag) ? dWarp1 : Double.valueOf(0);
JDK自己主动拆箱下,三目运算符的潜规则的更多相关文章
- Java的自动装箱/拆箱
概述 自JDK1.5开始, 引入了自动装箱/拆箱这一语法糖, 它使程序员的代码变得更加简洁, 不再需要进行显式转换.基本类型与包装类型在某些操作符的作用下, 包装类型调用valueOf()方法将原始类 ...
- 深入解析Java中的装箱和拆箱
自己主动装箱和拆箱问题是Java中一个老生常谈的问题了,今天我们就来一些看一下装箱和拆箱中的若干问题.本文先讲述装箱和拆箱最主要的东西,再来看一以下试笔试中常常遇到的与装箱.拆箱相关的问题. 下面是本 ...
- [Java开发之路](23)装箱与拆箱
1. 简单介绍 大家对基本数据类型都很熟悉.比如 int.float.double.boolean.char 等.基本数据类型是不具备对象的特性,比方基本类型不能调用方法.功能简单. ..,为了让基本 ...
- Java自动装箱和自动拆箱操作
1.Java数据类型 在介绍Java的自动装箱和拆箱之前,我们先来了解一下Java的基本数据类型. 在Java中,数据类型可以分为两大种,Primitive Type(基本类型)和Reference ...
- [转]JAVA自动装箱和拆箱
http://www.cnblogs.com/dolphin0520/p/3780005.html 1.Java数据类型 装箱和拆箱之前,我们先来了解一下Java的基本数据类型. 在Java中,数据类 ...
- JAVA进阶之旅(一)——增强for循环,基本数据类型的自动拆箱与装箱,享元设计模式,枚举的概述,枚举的应用,枚举的构造方法,枚举的抽象方法
JAVA进阶之旅(一)--增强for循环,基本数据类型的自动拆箱与装箱,享元设计模式,枚举的概述,枚举的应用,枚举的构造方法,枚举的抽象方法 学完我们的java之旅,其实收获还是很多的,但是依然还有很 ...
- java基础(七)-----深入剖析Java中的装箱和拆箱
本文主要介绍Java中的自动拆箱与自动装箱的有关知识. 基本数据类型 基本类型,或者叫做内置类型,是Java中不同于类(Class)的特殊类型.它们是我们编程中使用最频繁的类型. Java是一种强类型 ...
- Java包装类及其拆箱装箱
Java包装类,Wrapper~由于在java中,数据类型总共可分为两大种,基本数据类型(值类型)和类类型(引用数据类型).基本类型的数据不是对象,所以对于要将数据类型作为对象来使用的情况,java提 ...
- JAVA中拆箱和装箱
浅谈JAVA中拆箱与装箱 一. 什么是装箱?什么是拆箱? 在Java SE5之前,如果要生成一个数值为10的Integer对象,必须这样进行: Integer i = new Integer(10) ...
随机推荐
- MFC消息顺序
1.AfxWndProc() 该函数负责接收消息,找到消息所属的CWnd对象,然后调用AfxCallWndProc 2.AfxCallWndProc() 该函数负责保存消息(保存的内容主要 ...
- cocos2D(二)---- cocos2D文档的使用
在使用cocos2d进行游戏开发的过程中,难免要查阅cocos2d的API文档.搞清楚怎么使用某个类或者某个方法.幸运的是,cocos2d的作者已经在源码里面加入了文档凝视,我们仅仅须要使用文档生成工 ...
- Android Intent 解析之二
服务端Intent运行过程: Sticky:这个类型的BroadCast表示某些Intent须要被保留,当新的应用起来后,须要关注这个消息,可是呢,又不须要启动这个应用来接收此消息,比方耳机插入等消息 ...
- c++,派生类对象可以对基类赋值,基类对派生类不可以赋值
派生类对象可以对基类对象赋值,赋值时属于派生类独有的部分就舍弃不用. #include <iostream> using namespace std; class DemoA { publ ...
- c++子类和父类成员函数重名
子类和父类返回值参数相同,函数名相同,有virtual关键字,则由对象的类型决定调用哪个函数. 子类和父类只要函数名相同,没有virtual关键字,则子类的对象没有办法调用到父类的同名函数,父类的同名 ...
- BZOJ 1263: [SCOI2006]整数划分( 高精度 )
yy一下发现好像越小越好...分解成3*3*3*3……这种形式是最好的...然后就是高精度了 ----------------------------------------------------- ...
- [转]CentO下限制SSH登录次数
应公司内部网站等级测评的需求,正逐渐加强系统安全防护. 设备默认 3 次验证失败自动退出,并且结束会话:网络登录连接超时自动退出时间 5 分钟: 第一种方法:已验证. 1.ssh超时时间设置 # cd ...
- QT实现图片按钮(用qss切割图片,或者放三张图片)
我在网上找了很久,把他综合了一下 不说了关键代码来了:(这是一张图片切图的效果) void SetButtonStyle(QPushButton *button, QString imgsrc, in ...
- 基于visual Studio2013解决C语言竞赛题之0412水仙花数
题目 解决代码及点评 按照题目要求,3位数是从100~999,那么我们设计一个for循环遍历所有三位数 对每个三位数进行水仙花数的判断即可 /******************** ...
- Asp.Net中GridView加入鼠标滑过的高亮效果和单击行颜色改变
转载自:http://www.cnblogs.com/fly_dragon/archive/2010/09/03/1817252.html protected void GridView1_RowDa ...