文档章节

通过三目运算符来看JAVA的自动拆箱和装箱

nazir
 nazir
发布于 2016/04/27 10:19
字数 1035
阅读 47
收藏 0

近期碰到一个比较常见的错误: 空指针! Exception in thread “main” java.lang.NullPointerException,虽然有些空指针问题是很容易可以避免的,比如在eclipse上装一个Find Bugs插件就可以检测空指针。但有些空指针是插件检测不出来的,同时对于初学的java开发者来说,仅凭肉眼是很难看出来的。比如像下面的这段代码:

public class Test {public static void main(String args[]) {
        House house = new House();int doorNum = house != null ? house.getDoorNum() : 0;
    }
}
class House {
    Integer doorNum;public Integer getDoorNum() {return doorNum;
    }
}

这里用到的三目运算符是在java代码中很常见的,可以算是对if-else的一种简化写法。
因此把这句代码可以翻译成这样子:

int doorNum;if (house != null) {
      doorNum = house.getDoorNum();
} else {
      doorNum = 0;
}

这里需要先大概介绍拆箱和装箱的概念:
装箱:把基本类型用它们相应的引用类型包装起来,使其具有对象的性质。int包装成Integer、float包装成Float
拆箱:和装箱相反,将引用类型的对象简化成值类型的数据

这样一看错误就比较明显了,因为当执行doorNum = house.getDoorNum();这句代码时,编译器执行了自动拆箱,所以事实上虚拟机执行的是doorNum = house.getDoorNum().intValue();因此,当house.getDoorNum()null时,就会报空指针错误了。所以如果想要把house.getDoorNum()的值付给doorNum,必须同时判断house.getDoorNum()是否为空,那么就需要两个判空的逻辑,似乎太麻烦。于是我想到了把doorNum进行封装,变成Integer类型,那么当执行doorNum = house.getDoorNum();这句代码时,不就不用自动拆箱了嘛?问题不就解决了嘛?因为这样子如果翻译成if-else不就等于这样了嘛:

Integer doorNum;if (house != null) {
      doorNum = house.getDoorNum();
} else {
      doorNum = 0;
}

这样看起来没有什么问题了啊,但是不幸的是,空指针错误依旧存在。
于是再去看反编译文件,此时反编译后的代码已经变成了如下:

Integer doorNum = Integer.valueOf(house != null ? house.getDoorNum().intValue() : 0);

看了反编译文件,知道原来是我想当然了,三目运算符虽然很像if-else,但不完全一样。虽然把doorNum进行了封装,变成Integer类型。但是自动拆箱的步骤还是在,虚拟机是先执行了拆箱,接着又进行了自动装箱。那么为什么会这样呢?会不会和这个0有关呢?于是我把0也封装起来:

Integer doorNum = house != null ? house.getDoorNum() : new Integer(0);

果然问题解决了。此时三目运算的第二、三位全是封装类型,没有基本数据类型,所以没有拆箱和装箱了。但是当第二、三位中有一个是基本类型而另一个是封装类型时,就会执行自动拆箱。


三目运算符的语法定义如下:

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.


通过这个例子,大概总结出了以下几点,有不对的地方请指正:

1:这里只是举了Integer和int的例子,Long/long、Boolean/boolean等等也是同样的;
2:JAVA自动拆/装箱的确给开发者提供了许多便利,但是实际使用起来,还是有许多地方需要注意的;
3:虽然三目运算符看上去很简约,但是还是得注意正确使用,它不全等于if-else
4:当三目运算符中的第二、三位中,有一个是基本数据类型并且另一个是封装类型时,编译器就进行会自动拆箱;
5:有时候研究半天看似怎么都没有错的代码还不如看看反编译后的代码。

© 著作权归作者所有

nazir
粉丝 0
博文 20
码字总数 13313
作品 0
杭州
后端工程师
私信 提问
Java 自动拆箱和自动装箱学习笔记

Java 自动拆箱和自动装箱学习笔记 详情参考以下 深入剖析Java中的装箱和拆箱 JDK自动拆箱下,三目运算符的潜规则 Java 自动装箱与拆箱的实现原理 Autoboxing and Unboxing 1. 概述 Java 中的...

等到烟火清凉_
2018/08/25
44
0
你不知道的 equals 和 ==

先来看一道 equals和 == 相关的面试题吧。 先告诉你答案是 true,true,false,true。 i1 == i2 和 i1.equals(i2) 这两个都是 true,大多数人应该可以答对。后面的 i3 == i4 和 i3.equals(i4...

Wizey
2018/08/17
0
0
java核心基础 --- 基本数据类型

本篇博文主要介绍 java 基础数据类型、基本类型的数据转换、自动装箱拆箱机制。 1. 基础数据类型 整型 整型包含 byte(1字节)、short(2字节)、int(4字节)、long(8字节) 需要注意的是,...

firepation
01/24
53
0
Java自动装箱-拆箱机制究竟是什么

支持原文:http://tryenough.com/java-autobox 自动装箱和拆箱从Java 1.5开始引入,目的是将原始类型值转自动地转换成对应的对象。 什么是自动装箱和拆箱 自动装箱就是Java自动将原始类型值转...

TryEnough
03/13
0
0
深入浅出 Java 中的包装类

前阵子,我们分享了《Java中的基本数据类型转换》这篇文章,对许多粉丝还是有带来帮助的,今天讲一下 Java 包装类的的由来,及自动装箱、拆箱的概念和原理。 什么是包装类型 Java 设计当初就...

Java技术栈
2018/09/14
0
0

没有更多内容

加载失败,请刷新页面

加载更多

python数据结构

1、字符串及其方法(案例来自Python-100-Days) def main(): str1 = 'hello, world!' # 通过len函数计算字符串的长度 print(len(str1)) # 13 # 获得字符串首字母大写的...

huijue
6分钟前
0
0
OSChina 周日乱弹 —— 我,小小编辑,食人族酋长

Osc乱弹歌单(2019)请戳(这里) 【今日歌曲】 @宇辰OSC :分享娃娃的单曲《飘洋过海来看你》: #今日歌曲推荐# 《飘洋过海来看你》- 娃娃 手机党少年们想听歌,请使劲儿戳(这里) @宇辰OSC...

小小编辑
今天
735
10
MongoDB系列-- SpringBoot 中对 MongoDB 的 基本操作

SpringBoot 中对 MongoDB 的 基本操作 Database 库的创建 首先 在MongoDB 操作客户端 Robo 3T 中 创建数据库: 增加用户User: 创建 Collections 集合(类似mysql 中的 表): 后面我们大部分都...

TcWong
今天
40
0
spring cloud

一、从面试题入手 1.1、什么事微服务 1.2、微服务之间如何独立通讯的 1.3、springCloud和Dubbo有哪些区别 1.通信机制:DUbbo基于RPC远程过程调用;微服务cloud基于http restFUL API 1.4、spr...

榴莲黑芝麻糊
今天
26
0
Executor线程池原理与源码解读

线程池为线程生命周期的开销和资源不足问题提供了解决方 案。通过对多个任务重用线程,线程创建的开销被分摊到了多个任务上。 线程实现方式 Thread、Runnable、Callable //实现Runnable接口的...

小强的进阶之路
昨天
79
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部