更新时间:2020-02-28 10:28:42 来源:极悦 浏览1590次
三目运算符是我们经常在代码中使用的,a=(b==null?0:1);这样一行代码可以代替一个if-else,可以使代码变得清爽易读。但是,三目运算符也是有一定的语言规范的。在运用不恰当的时候会导致意想不到的问题。
一、三目运算符
对于条件表达式b?x:y,先计算条件b,然后进行判断。如果b的值为true,计算x的值,运算结果为x的值;否则,计算y的值,运算结果为y的值。一个条件表达式从不会既计算x,又计算y。条件运算符是右结合的,也就是说,从右向左分组计算。例如,a?b:c?d:e将按a?b:(c?d:e)执行。
二、自动装箱与自动拆箱
基本数据类型的自动装箱(autoboxing)、拆箱(unboxing)是自J2SE5.0开始提供的功能。
一般我们要创建一个类的对象实例的时候,我们会这样:Classa=newClass(parameters);当我们创建一个Integer对象时,却可以这样:Integeri=100;(注意:和inti=100;是有区别的)
实际上,执行上面那句代码的时候,系统为我们执行了:Integeri=Integer.valueOf(100);这里暂且不讨论这个原理是怎么实现的(何时拆箱、何时装箱),也略过普通数据类型和对象类型的区别。
我们可以理解为,当我们自己写的代码符合装(拆)箱规范的时候,编译器就会自动帮我们拆(装)箱。那么,这种不被程序员控制的自动拆(装)箱会不会存在什么问题呢?
三、问题回顾
首先,通过你已有的经验看一下下面这段代码。如果你得到的结果和后文分析的结果一致(并且你知道原理),那么请忽略本文。如果不一致,请跟我探索下去。
publicstaticvoidmain(String[]args){
Map<String,Boolean>map=newHashMap<>();
Booleanb=map!=null?map.get("test"):false;
System.out.println(b);
}
以上这段代码,是我们在不注意的情况下有可能经常会写的一类代码(在很多时候我们都爱使用三目运算符)。
一般情况下,我们会认为以上代码Booleanb的最终得到的值应该是null。因为map.get("test")的值是null,而b又是一个对象,所以得到结果会是null。
但是,以上代码会抛出NPE:
Exceptioninthread"main"java.lang.NullPointerException
首先可以明确的是,既然报了空指针,那么一定是有些地方调用了一个null的对象的某些方法。在这短短的两行代码中,看上去只有一处方法调用map.get("test"),但是我们也都是知道,map已经事先初始化过了,不会是Null,那么到底是哪里有空指针呢。
我们接下来反编译一下该代码。看看我们写的代码在经过编译器处理之后变成了什么样。反编译后代码如下:
publicstaticvoidmain(Stringargs[]){
Mapmap=newHashMap();
Booleanb=Boolean.valueOf(map==null?false:((Boolean)map.get("test")).booleanValue());
System.out.println(b);
}
看完这段反编译之后的代码之后,经过分析我们大概可以知道问题出在哪里。((Boolean)hashmap.get("test")).booleanValue()的执行过程及结果如下:
hashmap.get("test")->null;
(Boolean)null->null;
null.booleanValue()->报错
好,问题终于定位到了。很明显,上面源代码中的map.get("test")在被编译成了
(Boolean)map.get("test").booleanValue(),这是一种自动拆箱的操作。
那么,为什么这里会发生自动拆箱呢?这个问题又如何解决呢?
四、原理分析
通过查看反编译之后的代码,我们准确的定位到了问题,分析之后我们可以得出这样的结论:NPE的原因应该是三目运算符和自动拆箱导致了空指针异常。
那么,这段代码为什么会自动拆箱呢?这其实是三目运算符的语法规范。参见jls-15.25,摘要如下:
Ifthesecondandthirdoperandshavethesametype(whichmaybethenulltype),thenthatisthetypeoftheconditionalexpression.
IfoneofthesecondandthirdoperandsisofprimitivetypeT,andthetypeoftheotheristheresultofapplyingboxingconversion(§5.1.7)toT,thenthetypeoftheconditionalexpressionisT.
Ifoneofthesecondandthirdoperandsisofthenulltypeandthetypeoftheotherisareferencetype,thenthetypeoftheconditionalexpressionisthatreferencetype.
简单的来说就是:当第二,第三位操作数分别为基本类型和对象时,其中的对象就会拆箱为基本类型进行操作。
所以,结果就是:由于使用了三目运算符,并且第二、第三位操作数分别是基本类型和对象。所以对对象进行拆箱操作,由于该对象为null,所以在拆箱过程中调用null.booleanValue()的时候就报了NPE。
五、问题解决
如果代码这么写,就不会报错:
Map<String,Boolean>map=newHashMap<String,Boolean>();
Booleanb=(map!=null?map.get("test"):Boolean.FALSE);
就是保证了三目运算符的第二第三位操作数都为对象类型。这样就不会发生自动拆箱操作,以上代码得到的b的结果为null。
以上就是极悦注册机构小编介绍的“Javase全套视频:三目运算符”的内容,希望对大家有帮助,如有疑问,请在线咨询,有专业老师随时为你服务。
0基础 0学费 15天面授
Java就业班有基础 直达就业
业余时间 高薪转行
Java在职加薪班工作1~3年,加薪神器
工作3~5年,晋升架构
提交申请后,顾问老师会电话与您沟通安排学习