zoukankan      html  css  js  c++  java
  • 基本数据类型及其包装类(二)

    上篇文章我们简单介绍了包装的相关基本概念,并简单分析了 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 层面是没有变化的,这都是编译器做的「假象」。

    image

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

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

    面试题

    面试题一:

    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 的实现比较特殊,因为它只有两种取值可能,其实也能够算作支持缓存功能的。


    文章中的所有代码、图片、文件都云存储在我的 GitHub 上:

    (https://github.com/SingleYam/overview_java)

    欢迎关注微信公众号:扑在代码上的高尔基,所有文章都将同步在公众号上。

    image

  • 相关阅读:
    通过GetProcAddress函数动态调用dll中地函数,是否必须通过extern C声明导出函数?
    函数指针与typedef
    MSDN DLL 综合
    DLL
    Firefox浏览器兼容Javascript脚本的方法
    C++中extern “C”含义深层探索
    生成索引脚本
    使用Go语句生成数值表
    避免使用count(*)获得表的记录数,解决其延迟问题
    在程序开发中怎样写SQL语句可以提高数据库的性能
  • 原文地址:https://www.cnblogs.com/yangming1996/p/8834525.html
Copyright © 2011-2022 走看看