基本数据类型及其包装类(二)

上篇文章我们简单介绍了包装的相关基本概念,并简单分析了 Integer 类中的几个核心的方法源码,但是有关自动拆装箱的概念限于篇幅并没能完成介绍,本篇还将分析几种常见的包装类面试题,深入理解一下我们的包装类设计。

自动拆装箱

所谓「拆箱」就是指,包装类型转换为基本类型的过程,而所谓的「装箱」则是基本类型到包装类型的过程。例如:

public static void main(String[] args){
    int age = 21;
    Integer integer = new Integer(age);    //装箱
    int num = integer.intValue();          //拆箱
}

自从 jdk1.5 以后,引入了自动拆装箱的概念,上述代码可以简化成如下代码:

public static void main(String[] args){
    int age = 21;
    Integer integer = age;              //自动装箱
    int num = integer;                  //自动拆箱
}

是不是感觉简便了很多,但是实际上在 JVM 层面是没有变化的,这都是编译器做的「假象」。

只是编译器允许你这样书写代码了,其实编译成字节码指令的时候,编译器还是会调用相应的拆装箱方法的。

可以看到,拆装箱是需要方法调用的,也就是需要栈帧的入栈出栈的,直白点说,就是耗资源,所以我们的程序中应当尽量避免大量的「拆装箱」操作。

面试题

面试题一:

public static void main(String[] args){
    Integer i1 = 100;
    Integer i2 = 100;
    Integer i3 = 200;
    Integer i4 = 200;

    System.out.println(i1==i2);
    System.out.println(i3==i4);
}

如果之前没了解过 Integer 内部源码的人想必会对输出的结果「百思不得其解」。

输出结果为:

true
false

如果你认真看完了我的两篇文章,这个问题应该不难解释。

直接将整型数值赋值给 Integer 实例将发生装箱操作,也就是调用 valueOf 方法,而这个方法我们分析过,会首先检查一下 100 是否在缓存池是否缓存了,当然 IntegerCache 会默认缓存 [-128,127] 之间的 Integer 实例,所以这里会直接从缓存池中取出引用赋值给变量 i1 。

同理 i2 也会从缓存池中取引用,并且两者的引用的是同一个堆对象,所以才会输出 「true」。

而第二个输出「false」也是很好理解的,因为 200 不再缓存池缓存的范围内,所以每次调用 valueOf 方法都会新建一个不同的 Integer 实例。

面试题二:

public static void main(String[] args){
    Double i1 = 100.0;
    Double i2 = 100.0;
    Double i3 = 200.0;
    Double i4 = 200.0;

    System.out.println(i1==i2);
    System.out.println(i3==i4);
}

很多人会认为这段代码的输出结果会和上题一样,但是其实不然:

false
false

那是因为 Double 这个包装类并没有缓存池的概念,也就是说它会为每一个 double 型数值包装一个新的 Double 实例。正如它的 valueOf 方法:

public static Double valueOf(double d) {
    return new Double(d);
}

这里可能有人会疑问了,为什么 Integer 用缓存池提升效率,而 Double 却弃之不用呢?

其实也很简单,你会发现 IntegerCache 是用 Integer 数组缓存了某个区间的所有数值对应的 Integer 实例,那么请问给定一个区间 [-128.0,127.0],你能确定之中有多少个 double 数值吗?

因为任意一个区间,哪怕再小的区间都对应的无穷尽的 double 小数,所以无法进行缓存。

同样的问题也适用于 Float。

面试题三:

public static void main(String[] args){
    Boolean i1 = false;
    Boolean i2 = false;
    Boolean i3 = true;
    Boolean i4 = true;

    System.out.println(i1==i2);
    System.out.println(i3==i4);
}

输出结果:

true
true

Boolean 的 valueOf 方法是这样的:

public static final Boolean TRUE = new Boolean(true);

public static final Boolean FALSE = new Boolean(false);

public static Boolean valueOf(boolean b) {
    return (b ? TRUE : FALSE);
}

显然,结果相信不再需要多做解释了。

最后需要提一下的是,八种包装类中有以下五种是支持「缓存池」的。

  • Integer:对应的缓存池类型为 IntegerCache
  • Byte:对应的缓存池类型为 ByteCache
  • Short:对应的额缓存池类型为 ShortCache
  • Long:对应的额缓存池类型为 LongCache
  • Character:对应的缓存池类型为 CharacterCache

其实 Boolean 的实现比较特殊,因为它只有两种取值可能,其实也能够算作支持缓存功能的。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏大闲人柴毛毛

深入理解java异常处理机制

 1. 引子        try…catch…finally恐怕是大家再熟悉不过的语句了,而且感觉用起来也是很简单,逻辑上似乎也是很容易理解。不过,...

2454
来自专栏mathor

异常的捕获与处理

1762
来自专栏java学习

Java每日一练(2017/8/21)

每日一句 学的到东西的事情是锻炼,学不到的是磨练。 查看以前的所有练习题目以及答案:https://mp.weixin.qq.com/mp/homepage?_...

36016
来自专栏专注 Java 基础分享

java基础之继承(二)

上篇我们介绍了java中的构造方法,了解了关键字this和super在继承中所起到的作用,this可以显式调用重载的构造方法,super可以显式的调用父类中的任...

2108
来自专栏用户2442861的专栏

深入理解java异常处理机制

http://blog.csdn.net/hguisu/article/details/6155636

902
来自专栏java工会

Java基础第二阶段知识点,招初级java的面试官都在问这些

1594
来自专栏对角另一面

读 zepto 源码之工具函数

Zepto 提供了丰富的工具函数,下面来一一解读。 源码版本 本文阅读的源码为 zepto1.2.0 $.extend $.extend 方法可以用来扩展目标对...

2090
来自专栏专注数据中心高性能网络技术研发

[Effective Modern C++(11&14)]Chapter 3: Moving to Modern C++

2376
来自专栏小樱的经验随笔

C++STL中map容器的说明和使用技巧(杂谈)

1、map简介 map是一类关联式容器。它的特点是增加和删除节点对迭代器的影响很小,除了那个操作节点,对其他的节点都没有什么影响。对于迭代器来说,可以修改实值,...

3365
来自专栏Golang语言社区

深入分析golang多值返回以及闭包的实现

一、前言 golang有很多新颖的特性,不知道大家的使用的时候,有没想过,这些特性是如何实现的?当然你可能会说,不了解这些特性好像也不影响自己使用golang,...

5186

扫码关注云+社区

领取腾讯云代金券