java装箱与拆箱的深入理解

2016-09-04  本文已影响517人  zivxia

Java有一个类型系统有两个部分组成,包含基本类型(byte、char、int、short、long、float、double、boolean)和引用类型。而基本类型则对应着各自的引用类型,称为装箱的基本类型。而引用类型对应着各自的基本类型,称为拆箱的基本类型。对应的类型为:(Byte、Character、Integer、Short、Long、Float、Double、Boolean)

下面一具体例子来说明装箱与拆箱

//java 1.5之前创建一个Integer对象
Integer i = new Integer(10);
//java 1.5之后有了装箱的特性,直接用下列方式生成一个Integer对象
//在这个过程中会将int 类型的10自动装箱成为Integer类型
Integer i = 10;
//拆箱 输出的值为20,这个过程中,会先将Integer类型的j自动拆箱为基本类型的10,最后完成运算
Integer j = new Integer(10);
int k = 10;
System.out.print(j+k);

下面来说说基本数据类型与其对应的引用类型的区别:

  1. 基本类型只有值,而装箱基本类型既具有值也具有它们对象的同一性(就是两个装箱的基本类型具有相同的值和不同的同一性(对象不一样))
  2. 基本类型只有功能完备的值,而每个装箱类型不仅具有完备的值还具有所有功能值之外的null。
  3. 基本类型通常比装箱基本类型更节省时间和空间。

下面来举一个《Effective java中》的一个例子进行分析

Comparator<Integer> comparator = new Comparator<Integer>()
{
    public int compareTo(Integer first,Integer second)
    {
        return first > second ? 1:first == second ? 0 : -1;
    }

}
//这两个Integer实例都表示相同的值42
Integer a = new Integer(42);
Integer b = new Integer(42);
int c = comparator.compareTo(a,b);
//返回值多少 0 ? 但实际结果却是-1,难道 42 < 42 ?

下面就来解释上面例子的原因:
首先执行 a>b,两个Integer都自动拆箱为42,不成立,则执行 a==b,乍一看,没什么问题,42==42没问题啊,但实际上这是违反了了装箱的同一性,因为a和b都是引用类型,根据上述两个装箱类型具有相同的值和不同的同一性,只是两个对象的比较,即这两个对象是不同对象,所对应的内存也不一样,a!=b。所有正确答案应该是 -1。

现在来说说装箱和拆箱是怎么实现的:

public static void main(String[] args){
  Integer i = 10;
  int j = 10;
  System.out.print("i+j = "+(i+j));
}

打印结果为:

Paste_Image.png

反编译class文件得到:

Paste_Image.png

第一行是装箱过程,查看源码实际是调用了Integer.valueOf(int)方法返回一个Integer实例
第三行进行加法运算的时候是拆箱过程,将Integer实例i拆箱为int型,然后进行加法运算,实际是调用了Integer.intValue()方法返回一个int型数据。

关于装箱与拆箱大致的都讲的差不多了。下面举几个面试中关于装箱与拆箱会问到的例子:

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

      System.out.println(i1==i2); //返回true
      System.out.println(i3==i4); //返回false
 }
}

下面来解释下这个原因

//这是装箱的源码
public static Integer valueOf(int i) {    
     return  i >= 128 || i < -128 ? new Integer(i) : SMALL_VALUES[i + 128];}

/** * A cache of instances used by {@link Integer#valueOf(int)} and auto-boxing */
private static final Integer[] SMALL_VALUES = new Integer[256];
static {    
         for (int i = -128; i < 128; i++){        
                SMALL_VALUES[i + 128] = new Integer(i);    
         }
}
//当值在[-128,127)之间时,装箱操直接取已经cache好的Integer实例,所以i1==i2,当大于127时,每次都是new出来的Integer,指向不同的对象,所以i3!=i4
public class Main {
    public static void main(String[] args) {
 
        Integer a = 1;
        Integer b = 2;
        Integer c = 3;
        Integer d = 3;
        Integer e = 321;
        Integer f = 321;
        Long g = 3L;
        Long h = 2L;
         
        System.out.println(c==d);  //true
        System.out.println(e==f);   // false
        System.out.println(c==(a+b)); // true
        System.out.println(c.equals(a+b));//true
        System.out.println(g==(a+b));//false
        System.out.println(g.equals(a+b));//false
        System.out.println(g.equals(a+h));//true
    }
}


// Example 1: == comparison pure primitive – no autoboxing
        int i1 = 1;
        int i2 = 1;
        System.out.println("i1==i2 : " + (i1 == i2)); // true

        // Example 2: equality operator mixing object and primitive
        Integer num1 = 1; // autoboxing
        int num2 = 1;
        System.out.println("num1 == num2 : " + (num1 == num2)); // true

        // Example 3: special case - arises due to autoboxing in Java
        Integer obj1 = 1; // autoboxing will call Integer.valueOf()
        Integer obj2 = 1; // same call to Integer.valueOf() will return same
        // cached Object

        System.out.println("obj1 == obj2 : " + (obj1 == obj2)); // true

        int obj3 = 3;
        Integer obj4 = 3;
        Integer obj5 = new Integer(3);

        System.out.println("obj4 == obj5 :  " + (obj4 == obj5));//false
        System.out.println("obj3 == obj5 :  " + (obj3 == obj5));//true

        // Example 4: equality operator - pure object comparison
        Integer one = new Integer(1); // no autoboxing
        Integer anotherOne = new Integer(1);

        System.out.println("one == anotherOne : " + (one == anotherOne)); // false

//当 "=="运算符的两个操作数都是 包装器类型的引用,则是比较指向的是否是同一个对象,而如果其中有一个操作数是表达式(即包含算术运算)则比较的是数值(即会触发自动拆箱的过程)。另外,对于包装器类型,equals方法并不会进行类型转换
上一篇下一篇

猜你喜欢

热点阅读