资讯专栏INFORMATION COLUMN

JAVA 三目运算时遇到的坑

jasperyang / 235人阅读

摘要:但是,三目运算符也是有一定的语言规范的。一三目运算符对于条件表达式,先计算条件,然后进行判断。那么,这段代码为什么会自动拆箱呢这其实是三目运算符的语法规范。所以,结果就是由于使用了三目运算符,并且第二第三位操作数分别是基本类型和对象。

三目运算符是我们经常在代码中使用的,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)是自J2SE 5.0开始提供的功能。

一般我们要创建一个类的对象实例的时候,我们会这样: Class a = new Class(parameters); 当我们创建一个Integer对象时,却可以这样: Integer i = 100;(注意:和 int i = 100;是有区别的 )

实际上,执行上面那句代码的时候,系统为我们执行了: Integer i = Integer.valueOf(100); 这里暂且不讨论这个原理是怎么实现的(何时拆箱、何时装箱),也略过普通数据类型和对象类型的区别。

我们可以理解为,当我们自己写的代码符合装(拆)箱规范的时候,编译器就会自动帮我们拆(装)箱。那么,这种不被程序员控制的自动拆(装)箱会不会存在什么问题呢?

三、问题回顾

首先,通过你已有的经验看一下下面这段代码。如果你得到的结果和后文分析的结果一致(并且你知道原理),那么请忽略本文。如果不一致,请跟我探索下去。

public static void main(String[] args) {    
    Map map = new HashMap<>();    
    Boolean b = map != null ? map.get("test") : false;    
    System.out.println(b);
}

以上这段代码,是我们在不注意的情况下有可能经常会写的一类代码(在很多时候我们都爱使用三目运算符)。

一般情况下,我们会认为以上代码Boolean b的最终得到的值应该是null。因为map.get("test")的值是null,而b又是一个对象,所以得到结果会是null。

但是,以上代码会抛出NPE:

Exception in thread "main" java.lang.NullPointerException
首先可以明确的是,既然报了空指针,那么一定是有些地方调用了一个null的对象的某些方法。在这短短的两行代码中,看上去只有一处方法调用map.get("test"),但是我们也都是知道,map已经事先初始化过了,不会是Null,那么到底是哪里有空指针呢。

我们接下来[反编译]一下该代码。看看我们写的代码在经过编译器处理之后变成了什么样。反编译后代码如下:

public static void main(String args[]){   
    Map map = new HashMap();   
    Boolean b = 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,摘要如下:

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.

简单的来说就是:当第二,第三位操作数分别为基本类型和对象时,其中的对象就会拆箱为基本类型进行操作。

所以,结果就是:由于使用了三目运算符,并且第二、第三位操作数分别是基本类型和对象。所以对对象进行拆箱操作,由于该对象为null,所以在拆箱过程中调用null.booleanValue()的时候就报了NPE。

五、问题解决

如果代码这么写,就不会报错:

Map map =  new HashMap();
Boolean b = (map!=null ? map.get("test") : Boolean.FALSE);

就是保证了三目运算符的第二第三位操作数都为对象类型。这样就不会发生自动拆箱操作,以上代码得到的b的结果为null。

PS:本文中的示例,只是为了更加方便读者理解三目运算符会导致自动拆箱现象,可能在代码中并不会直接这样使用。但是,我自己的代码确实发生过类似问题。这里简化一下,为了讲清楚原理。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/69350.html

相关文章

  • 2.java运算符与逻辑控制

    摘要:如果条件为,则逻辑非运算符将得到进行逻辑判断的顺序为从左到右。再次检测布尔表达式。其作用域限定在循环语句块,其值与此时数组元素的值相等。 运算符和逻辑控制 运算符 java中的运算符可以分为以下几种: 算符运算符 关系运算符 位运算符 逻辑运算符 赋值运算符 三目运算符 算符运算符 操作符 描述 + 加法 - 减法 * 乘法 / 除法 % 取余(模) ...

    dingding199389 评论0 收藏0
  • Java猫说】每日算法:#2-三目运算符美观打印ASCII码

    摘要:每日算法题目将码位于之间的个字符显示在屏幕上,为了美观,要求小于的码值前填充一个,每打印个字符后换行。本文已转载个人技术公众号欢迎留言讨论与点赞上一篇推荐猫说每日算法实现特殊累加值下一篇推荐猫说每日算法枚举求就业率问题 Java每日算法 题目 将ASCII码位于32-126之间的95个字符显示在屏幕上,为了美观,要求小于100的码值前填充一个0,每打印8个字符后换行。 分析 这里先注意...

    everfly 评论0 收藏0
  • 十个你需要在 PHP 7 中避免的坑

    摘要:不要使用类函数终于,你不用再看到建议不要使用函数的提示了。因为从核心上完全移除了它们,这意味着请你移步至更好的类函数,或者更灵活的层。将从数据库获取一个元数据,如果您正在循环访问特定文章的元数据,则可以在循环中使用它。 showImg(https://segmentfault.com/img/bV75FM?w=1024&h=534); 1. 不要使用 mysql_ 类函数 终于,你不用...

    leanote 评论0 收藏0
  • Java知识点总结 (基本语法)

    摘要:强制类型转换下标运算符变量与常量常量是在程序中的不会变化的数据变量其实就是内存中的一个存储空间,用于存储数据。表示结束本次循环,继续下次循环。 Java知识点总结 (基本语法) @(Java知识点总结)[Java, Java基本语法] @(Java开发)[Java基本语法] [toc] Java特点 简单自然平台可移植性支持函数式编程JIT 编译更好的并发编程健壮安全 执行方式 编译...

    tuantuan 评论0 收藏0
  • 三目(三元)运算符??::的形式

    摘要:注意,三目运算符中和是成对出现的,最起码数量上,有几个就会有几个 三目运算符相信大家都很熟悉了: foo ? foo == true : foo == false 而三目运算符?:?:?.....的调用方式大家也不陌生, 就相当于一堆if - else if语句: foo ? foo == true : bar ? bar == true : bar == false 但是在zep...

    Yangyang 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<