美文网首页
JavaSE的自动装箱和自动拆箱

JavaSE的自动装箱和自动拆箱

作者: 静默加载 | 来源:发表于2017-05-30 17:21 被阅读50次

    个人博客地址 http://dandanlove.com/

    回顾一下Java基础数据类型:

    Java数据类型
    基础类型 字节 包装类型
    int 4字节 Integer
    byte 1字节 Byte
    short 2字节 Short
    long 8字节 Long
    float 4字节 Float
    double 8字节 Double
    char 2字节 Character
    boolean 未定 Boolean

    Java属于面向对象语言那么为什么会出现非对象类型数据(基础类型),因为基础数据类型是的虚拟机的运行速度更快而且占用内存更少。详情内容可以参见:Java为什么需要保留基本数据类型

    为什么要有装箱&拆箱

    在JavaSE5之前我们创建爱你Integer对象:

    Integer i = new Integer(10);
    

    从JavaSE5提供了自动装箱的特性时,我们可以更简单的创建基础类型的对象:

    Integer a = 10;
    int b = a;
    

    从上面的代码我们可以简单的看出装箱、拆箱的操作:
    Integer a = 10; 我们将10【装箱】生成Integer对象。
    int b = a; 我们将Integer【拆箱】转成int基础类型

    装箱和拆箱是如何实现的

    我们这里先写一个简单的类,然后反编译看看它的字节码文件

    public class Main {
        public static void main(String[] args) {
            Integer a = 10;
            int b = a;
        }
    }
    

    反编译出来的字节码文件:

    Main字节码.jpg

    结论:

    装箱操作:

    Integer a = 10;
    //实际执行的是Integer a = Integer.valueOf(10);
    

    拆箱操作:

    int b = a;
    //实际执行的是int b = a.intValue();
    

    其他&扩展

    我们先来看一道面试题:

    public class Main {
        public static void main(String[] args) {
            Integer a = 10;
            Integer b = 10;
            Integer c = 128;
            Integer d = 128;
            System.out.println(a == b);
            System.out.println(c == d);
        }
    }
    

    内心怀揣自己的真是答案,我们看看下边的源代码:
    先看看Integer装箱和拆箱的函数源码:

    /**
     * Returns the value of this {@code Integer} as an
     * {@code int}.
     */
    public int intValue() {
        return value;
    }
    
    /**
     * Returns an {@code Integer} instance representing the specified
     * {@code int} value.  If a new {@code Integer} instance is not
     * required, this method should generally be used in preference to
     * the constructor {@link #Integer(int)}, as this method is likely
     * to yield significantly better space and time performance by
     * caching frequently requested values.
     *
     * This method will always cache values in the range -128 to 127,
     * inclusive, and may cache other values outside of this range.
     *
     * @param  i an {@code int} value.
     * @return an {@code Integer} instance representing {@code i}.
     * @since  1.5
     */
    public static Integer valueOf(int i) {
        if (i >= IntegerCache.low && i <= IntegerCache.high)
            return IntegerCache.cache[i + (-IntegerCache.low)];
        return new Integer(i);
    }
    
    • 拆箱操作:直接返回Integer内的数值
    • 装箱操作:在i大于IntegerCache.low或者i小于IntegerCache.high时返回缓存的Integer对象,否则创建新的Integer对象。
    /**
     * Cache to support the object identity semantics of autoboxing for values between
     * -128 and 127 (inclusive) as required by JLS.
     *
     * The cache is initialized on first usage.  The size of the cache
     * may be controlled by the {@code -XX:AutoBoxCacheMax=<size>} option.
     * During VM initialization, java.lang.Integer.IntegerCache.high property
     * may be set and saved in the private system properties in the
     * sun.misc.VM class.
     */
    private static class IntegerCache {
        /*********/
    }
    

    通过源码码我们可以发现,Integer在数据为[-128,127]之间时。使用了IntegerCache 返回缓存中对象的引用,否则new一个新的对象。

    看到上面这个答案,有些同学就会想到:除过Integer之前还有其他的基础数据类型,那么其他的类型是否也是专业那个的呢?答案:是也不是。原理想想大家也都明白:

    • Boolean内部有true&false两个静态变量,最后装箱得到的值都是这两个静态变量的引用。
    • Long&Integer&Short&Byte在数值为[-128,127]之间都有Cache。
    • Double&Float则都没有。

    所以上面问题的正确答案分别是:true、false。

    见识了"=="比较,现在看equals比较结果:
    同样我们也先看一道题目:

    public class Main {
        public static void main(String[] args) {
            Integer a = 1;
            Integer b = 2;
            Long c = 3L;
            System.out.println(c == (a + b));
            System.out.println(c.equals(a + b));
        }
    }
    

    然后我们再看看源码:

    /**
     * Compares this object to the specified object.  The result is
     * {@code true} if and only if the argument is not
     * {@code null} and is an {@code Integer} object that
     * contains the same {@code int} value as this object.
     *
     * @param   obj   the object to compare with.
     * @return  {@code true} if the objects are the same;
     *          {@code false} otherwise.
     */
    public boolean equals(Object obj) {
        if (obj instanceof Integer) {
            return value == ((Integer)obj).intValue();
        }
        return false;
    }
    

    在Java中我们知道操作"=="的两个数都是数据包装类型对象的引用的话,那么则是用来比较两个引用所指向的对象是不是同一个;而如果其中有一个操作数是表达式(即包含算术运算)则比较的是数值(即会触发自动拆箱的过程)。为什么呢,因为"=="两边引用数据类型必须一致,要不然无语错误。

    所以我们得到上边题目的答案是:true、false。
    因为第一次比较实际是先对数据进行拆箱然后比较,所以得到的结果是true;第二次比较实际是先拆箱(两个Integer对象拆箱)后装箱(将拆箱且计算后的数据再装箱),然后同Long对象比较,显然不是同一类型所以得到false。

    以上问题及答案都是作者亲自敲出来的,想实际操作同学也可以反编译class文件看看【真相】’。

    相关文章

      网友评论

          本文标题:JavaSE的自动装箱和自动拆箱

          本文链接:https://www.haomeiwen.com/subject/jtuqfxtx.html