拆箱与装箱

一、何为包装类型

Java是一种面向对象的语言,但是它不是纯面向对象的。Java中存在基本数据类型,谈不上对象。为了向纯面向对象靠拢,Java5的时候推出了基本数据类型的包装类型。

基本数据类型与包装类型的对应关系如下: 

 


 

二、何为装箱与拆箱

装箱就是将基本数据类型转化为包装类型,那么拆箱就是将包装类型转化为基本数据类型。

以基本数据类型int为例:

package day1119;
 
public class TestBox {
    public static void main(String[] args) {
        //自动装箱,底层其实执行了Integer a=Integer.valueOf(1);
        Integer a = 1;
        //自动拆箱,底层其实执行了int b=a.intValue();
        int b = a;
    }
}

  

Integer的valueOf(int i)方法可以将一个基本数据类型转化为对应的包装类型,即装箱方法。

而Integer的intValue()方法则可以将一个包装类型转化为对应的基本数据类型,即拆箱方法。


三、装箱与自动装箱的区别

装箱:利用Integer的构造方法Integer(int value),即Integer c=new Integer(1);

自动装箱:或者叫隐式装箱,直接给Integer赋值,即Integer d=1,在编译的时候,会调用Integer.valueOf()方法完成装箱。

相比而言,自动装箱可能比装箱具有更高的效率,体现在自动装箱的缓存上,下面从几道题目来讲自动装箱的缓存。


四、相关面试题目

第一题:以下代码的输出结果为?(==号两边如果都是引用类型的话,则判断它们是否指向同一个对象。如果都是基本数据类型的话,则判断它们的数值是否相等)

package day1119;
 
public class TestBox2 {
    public static void main(String[] args) {
        Integer a = 100;
        Integer b = 100;
        Integer c = 200;
        Integer d = 200;
        System.out.println(a == b);
        System.out.println(c == d);
    }
}

  

也许有些人认为他们是四个各不相同的对象,两个式子都返回false。

实际运行后发现输出:

 

 

刚才我们知道,Integer a=100这条语句会触发自动装箱,而自动装箱的方法为Integer.valueOf()方法,让我们去寻找这个方法,一探究竟。

观察Integer类的源码中的valueOf()

    /**
     * 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);
    }

  

可以看得出,当i的值位于[-128,127]的时候,会直接返回Integer缓存数组中相应对象的引用,如果i大于127或小于-128,会重新创建一个Integer实例,并返回。

那么第一条式子a和b的值都在缓存范围内,因此他们指向同一个对象,因此返回true。c和d的值不在范围内,都是通过new创建出来的,因此不是同一个对象,返回false。

注意:Byte、Short、Integer、Long、Character的valueOf()实现机制类似。

其中相同值的Byte比较永远返回true,因为byte取值范围就是[-128,127]。

Short、Integer、Long的valueOf()基本一样,i的范围都需要在[-128,127]。

Character中i的范围只要小于等于127即可,因为char最小值为0,本来就大于等于-128。

但是Float、Double中的valueOf(),永远返回新创建的对象,因为一个范围内的整数是有限的,但是小数却是无限的,无法保存在缓存中。

Boolean中只有两个对象,要么是true的Boolean,要么是false的Boolean,只要boolean的值相同,Boolean就相等。


第二题:以下代码的输出结果为?

package day1119;
 
public class TestBox3 {
    public static void main(String[] args) {
        Integer a = 1;
        Integer b = 2;
        Integer c = 3;
        Long d = 2L;
        Long e = 3L;
        int f = 2;
 
        //一旦有包装类型和数值类型判断==时,则触发包装类型的自动拆箱,转为数值类型的比较
        System.out.println(new Integer(300) == 300);//返回true
 
        //一旦有包装类型和数值类型发生运算时,则触发包装类型的自动拆箱,转为数值类型的运算
        System.out.println(c == (a + f));//返回true
 
        //一旦有包装类型和包装类型发生运算时,则触发包装类型的自动拆箱,转为数值类型的运算
        System.out.println(c == (a + b));//返回true
 
        //只有对象类型才有equals方法,因此首先a,b触发包装类型的自动拆箱,转为数值类型的运算。
        //运算完,再将结果3自动装箱,Integer重写了equals,因此可以转为包装类型与包装类型的比较。
        //当两边的包装类型不一致时,必定返回false。
        //当两边的包装类型一致时,再进行拆箱,判断两者代表的数值是否相等。
        System.out.println(c.equals(a + b));//返回true
 
        //不同数据类型的数值进行运算,首先会将低精度的数据类型转化为高精度的数据类型,即自动类型转换。
        //比如现在的int+long,会提升到long+long,再进行运算。
        System.out.println(e == (a + d));//返回true
 
        //==号两边类型不一致时,直接执行自动拆箱,比较之后的数值
        System.out.println(e == (a + b));//返回true
 
        //依次经历自动拆箱,自动类型转换、运算、自动装箱,类型比较,拆箱,数值比较
        System.out.println(e.equals(a + d));//返回true
 
        //依次经历自动拆箱,自动类型转换、运算、自动装箱,类型比较,两边类型不一致,直接返回false
        System.out.println(c.equals(a + d));//返回false
 
 
    }
}

  

 

posted @ 2020-08-31 22:07  Roy-Xin  阅读(364)  评论(0编辑  收藏  举报