你真的了解Java中的三目运算符吗

转载自 你真的了解Java中的三目运算符吗

三目运算符是我们经常在代码中使用的,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<StringBoolean> 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<String,Boolean> map =  new HashMap<StringBoolean>();
Boolean b = (map!=null ? map.get("test") : Boolean.FALSE);

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

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


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/329670.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

关于.NET下开源及商业图像处理(PSD)组件

1 前言 这篇博客的背景是&#xff1a;为了完成吉日嘎拉的“PSD文件损坏检测和图层检测”任务&#xff0c;查找了目前.NET各种开源的及商业的图像处理资料&#xff0c;在完成任务之后&#xff0c;进行总结。此次任务主要是用C#操作PSD(PhotoShop)文件&#xff0c;中文资料很少&a…

SpringBoot连接多RabbitMQ源

转自&#xff1a; SpringBoot连接多RabbitMQ源 - 掘金在实际开发中&#xff0c;很多场景需要异步处理&#xff0c;这时就需要用到RabbitMQ&#xff0c;而且随着场景的增多程序可能需要连接多个RabbitMQ。SpringBoot本身提供了默认的配置可以快速配置连接RabbitMQ&#xff0c;但…

满足其中一个条件则可_农村分户好处多,但并非人人都可分户!满足这4个条件才可以申请...

分户&#xff0c;一般指子女成年或者成家后从父母户口里面独立出去&#xff0c;自立一户&#xff1b;也可以是指夫妻离婚后一方将户口独立出去(离婚也可以不分户&#xff0c;变更婚姻状态就行)。简单的说分户是指原本在一个户口本上的人口&#xff0c;现在分出去自成一个户口本…

Synchronized的实现原理(一)

转载自 Synchronized的实现原理&#xff08;一&#xff09;synchronized&#xff0c;是Java中用于解决并发情况下数据同步访问的一个很重要的关键字。当我们想要保证一个共享资源在同一时间只会被一个线程访问到时&#xff0c;我们可以在代码中使用synchronized关键字对类或者对…

FOSS历史回顾:三代开源人的故事

现在是2016年&#xff0c;你环顾一下四周&#xff0c;开源早已无处不在了。开源无论是规范、形式、以及面貌都和最初的大相径庭&#xff0c;然而事实上&#xff0c;这也预示着新一代的开源程序员们的崛起。下面我们尝试解释下。 &#xff08;以下这一段落为作者自谦&#xff09…

Spring中@Autowired、@Qualifier、@Resource的区别

转自&#xff1a; Spring中Autowired、Qualifier、Resource的区别_老周聊架构的博客-CSDN博客_qualifier和resource区别1、AutowiredAutowired 可以单独使用。如果单独使用&#xff0c;它将按类型装配。因此&#xff0c;如果在容器中声明了多个相同类型的bean&#xff0c;则会…

map分组后取前10个_海关数据 | 图解前10个月外贸

*内容转载自微信公众号&#xff1a;海关发布RECOMMEND【 推荐阅读 】海关数据 | 图解前三季度我国外贸海关数据 | 图解8月外贸海关数据 | 一图看懂前7个月外贸海关数据 | 图解上半年度外贸增3.9%声明本微信订阅号不以商业营利为目的&#xff0c;不排除部分文字内容或图片转载自…

回顾build 2016:你好,这是微软迄今最好的Windows开发平台

按&#xff1a;本文作者陈计节&#xff0c;ThoughtWorks 高级咨询师。多年的跨平台 .NET 开发者&#xff0c;全栈工程师&#xff0c;技术布道师。擅长互联网应用程序的设计、开发和运维等工作。 在最近的开发者大会&#xff08;Build 2016&#xff09;上&#xff0c;微软面向开…

深入理解多线程(二)—— Java的对象模型

转载自 深入理解多线程&#xff08;二&#xff09;—— Java的对象模型上一篇文章中简单介绍过synchronized关键字的方式&#xff0c;其中&#xff0c;同步代码块使用monitorenter和monitorexit两个指令实现&#xff0c;同步方法使用ACC_SYNCHRONIZED标记符实现。后面几篇文章会…

8.1-CPU结构(学习笔记)

【README】 本文总结自bilibili《计算机组成原理&#xff08;哈工大刘宏伟&#xff09;》的视频讲解&#xff0c;非常棒&#xff0c;墙裂推荐&#xff1b; 【1】CPU结构 Cpu的首要功能就是解释指令&#xff1b;功能列表如下&#xff1a; 1) 取指令&#xff1a;从内存中读取…

生物信息 python 书籍_用python做生物信息数据分析(1-环境准备)

写在前面四五年前&#xff0c;接触生物信息的时候&#xff0c;阴差阳错&#xff0c;我选择用perl。事实上&#xff0c;直到嫌我&#xff0c;我还是认为我当初的选择&#xff0c;完全正确&#xff01;。在做一些小文本的快速处理上&#xff0c;perl在我看来&#xff0c;从来最优…

8.2-指令周期(学习笔记)

【README】 本文总结自bilibili《计算机组成原理&#xff08;哈工大刘宏伟&#xff09;》的视频讲解&#xff0c;非常棒&#xff0c;墙裂推荐&#xff1b; 【1】指令周期 【1.1】指令周期概述 1&#xff09;指令周期&#xff1a;取出并执行一条指令所需的全部时间&#xff1…

深入理解多线程(三)—— Java的对象头

转载自 深入理解多线程&#xff08;三&#xff09;—— Java的对象头上一篇文章中我们从HotSpot的源码入手&#xff0c;介绍了Java的对象模型。这一篇文章在上一篇文章的基础上再来介绍一下Java的对象头。主要介绍一下对象头的作用&#xff0c;结构以及他和锁的关系。 Java对象…

python tkinter 背景色改变不了_python - Tkinter背景颜色问题 - 堆栈内存溢出

我有一个脚本&#xff0c;其中包含Tkinter模块&#xff0c;我想每隔3分钟更改一次背景颜色&#xff0c;例如绿色3分钟&#xff0c;然后橙色&#xff0c;然后红色。 我有显示绿色的代码&#xff0c;但无法更改它。当我在代码中创建函数时&#xff0c;会遇到一些不同的错误&#…

回顾微软近年来对于Linux和开源的策略

2014年十月&#xff0c;在旧金山举办的一场活动中&#xff0c;微软的CEO Satya Nadella向公众表示&#xff0c;微软“爱Linux”。作为昔日的竞争对手&#xff0c;微软对Linux的态度逐渐从敌对转变为合作。自那次发言以来&#xff0c;微软在开源方面频频重拳出击&#xff0c;似乎…

深入理解多线程(四)—— Moniter的实现原理

转载自 深入理解多线程&#xff08;四&#xff09;—— Moniter的实现原理本文是《深入理解多线程系列文章》的第四篇。点击查看原文&#xff0c;阅读该系列所有文章。 在深入理解多线程&#xff08;一&#xff09;——Synchronized的实现原理中介绍过关于Synchronize的实现原理…

(转)Spring Boot通过ImportBeanDefinitionRegistrar动态注入Bean

转自&#xff1a; Spring Boot通过ImportBeanDefinitionRegistrar动态注入Bean - 掘金在阅读SpringBoot源码时&#xff0c;看到SpringBoot中大量使用ImportBeanDefinitionRegistrar来实现Bean的动态注入。它是Spring中一个强大的扩展接口。本篇文章来https://juejin.cn/post/6…

通过图书编号查询python_Python图书接口调用代码实例

1.[代码][Python]代码#!/usr/bin/python# -*- coding: utf-8 -*-import json, urllibfrom urllib import urlencode#----------------------------------# 图书电商数据调用示例代码 &#xff0d; 聚合数据# 在线接口文档&#xff1a;http://www.juhe.cn/docs/50#-------------…

深入理解多线程(五)—— Java虚拟机的锁优化技术

转载自 深入理解多线程&#xff08;五&#xff09;—— Java虚拟机的锁优化技术本文是《深入理解多线程》的第五篇文章&#xff0c;前面几篇文章中我们从synchronized的实现原理开始&#xff0c;一直介绍到了Monitor的实现原理。这一篇在前几篇的基础上&#xff0c;深入介绍一下…

Visual Studio Code 1.0正式发布

Visual Studio Code 是一个运行于 OS X&#xff0c;Windows 和 Linux 之上的&#xff0c;针对于编写现代 web 和云应用的跨平台编辑器。 这标志着 Microsoft 第一次向开发者们提供了一款真正的跨平台编辑器。虽然完整版的 Visual Studio 仍然是只能运行在 Windows 之上&#xf…