Java中的装箱和拆箱

装箱和拆箱介绍

什么是自动装箱拆箱

 基本数据类型的自动装箱(autoboxing)、拆箱(unboxing)是自J2SE 5.0开始提供的功能。 

   一般我们要创建一个类的对象实例的时候,我们会这样:

       Class a = new Class(parameter);

   当我们创建一个Integer对象时,却可以这样:

     Integer i = 100; (注意:不是 int i = 100; )

 实际上,执行上面那句代码的时候,系统为我们执行了:Integer i = Integer.valueOf(100); 

 此即基本数据类型的自动装箱功能。

基本数据类型与对象的差别 

 基本数据类型不是对象,也就是使用int、double、boolean等定义的变量、常量。

  基本数据类型没有可调用的方法。

  例如:  int t = 1;     t.  后面是没有方法滴。

            Integer t =1; t.  后面就有很多方法可让你调用了。

 

 首先了解下Java的四类八种基本数据类型

基本类型

占用空间(Byte)

表示范围

包装器类型

boolean

1/8

true|false

Boolean

char

2

-128~127

Character

byte

1

-128~127

Byte

short

2

-2ˆ15~2ˆ15-1

Short

int

4

-2ˆ31~2ˆ31-1

Integer

long

8

-2ˆ63~2ˆ63-1

Long

float

4

-3.403E38~3.403E38

Float

double

8

-1.798E308~1.798E308

Double

 二.装箱和拆箱是如何实现的

 自动装箱 

   Java中所谓的装箱通俗点就是:八种基本数据类型在某些条件下使用时,会自动变为对应的包装器类型。

   如下清单1:

 public void boxingTest() {
    Integer i1 = 17;
        Integer i2 = 17; 
        Integer i3 = 137;
        Integer i4 = 137;
      System.out.println(i1 == i2); //true
      System.out.println(i3 == i4);  //false
  }

   blob.png

   从源码中可以看出,Integer对象自动缓存int值范围在low~high(-128~127),如果超出这个范围则会自动装箱为包装类。

  Note:

    1. Integer、Short、Byte、Character、Long这几个包装类的valueOf方法的实现是类似的;

    2. Double、Float的valueOf方法的实现是类似的。

    3. Boolean的valueOf方法的实现是个三目运算,形如`  return (b ? TRUE : FALSE);  `

自动拆箱

  Java中所谓的拆箱通俗点就是:八种包装器类型在某些条件下使用时,会自动变为对应的基本数据类型。

  清单2:

   

 public void unboxingTest() {
        Integer i1 = 17;
        int i2 = 17; 
        int i3 = 137;
        Integer i4 = 137;
        System.out.println(i1 == i2);   
    10  System.out.println(i3 == i4);
 
 }

  输出:true true

  解释下清单2第10句输出true的原因:

  当程序执行到第10句时,i4会调用Integer.intValue方法自动拆箱包装器类型为基本数据类型。

 

  /**
     * Returns the value of this {@code Integer} as an
     * {@code int}.
     */
    public int intValue() {
        return value;
    }

  从源码可以看出,当包装器类型和基本数据类型进行“==”比较时,包装器类型会自动拆箱为基本数据类型。

  清单3内容如下:     

  public void unboxingTest() {
    Integer i1 = 17;
    Integer i2 = 17; 
    Integer i3 = 137;
    Integer i4 = 137;
    // == 
    System.out.println(i1 == i2);
    System.out.println(i3 == i4);
    // equals
    System.out.println(i1.equals(i2));
 15  System.out.println(i3.equals(i4));
 }

  

 解释第15句为什么会输出true:

  因为在Integer包装类实现的equals方法中,只要比较的当前对象是Integer实例,那么就会自动拆箱为基本数据类型。从以下Integer类的equals方法的源码就可看出:

   

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

 Note:

    1. Integer、Short、Byte、Character、Long这几个包装类的intValue方法的实现是类似的;

    2. Double、Float的intValue方法的实现是类似的。

    3. Boolean的booleanValue方法的实现和intValue方法的实现也是类似的。

 

  装箱拆箱综合清单:

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
        
        // 虽然“==”比较的是引用的是否是同一对象,但这里有算术运算,如果该引用为包装器类型则会导致自动拆箱(a+b)拆箱intValue() value=3 
        // 相加后的值是拆箱后的基本类型,基本类型与包装类比较时,会进行拆箱。
        System.out.println(c==(a+b));
        // equals 比较的是引用的对象的内容(值)是否相等,但这里有算术运算,如果该引用为包装器类型则会导致自动拆箱intValue,再自动装箱valueOf();
        // a+b触发自动拆箱得到值后,再自动装箱与c比较,又因为Integer实现了equals方式( return value == ((Integer)obj).intValue();)。
        System.out.println(c.equals(a+b));
        // 首先a+b触发自动拆箱后值为int型,所以比较的是值是否相等
        System.out.println(g==(a+b));
        // 首先a+b触发自动拆箱后值为int型,自动装箱后为Integer型,然后g为Long型 
        System.out.println(g.equals(a+b));
        // 首先a+h触发自动拆箱后值为long型,因为int型的a会自动转型为long型的g然后自动装箱后为Long型,
        // 而g也为Long型 
        System.out.println(g.equals(a+h));
    
}

输出:true false true true true false true

这里面需要注意的是: "=="运算符的两个操作数都是包装器类型的引用,则是比较指向的是否是同一个对象,而如果其中有一个操作数是表达式(即包含算术运算)则比较的是数值(即会触发自动拆箱的过程)

另外,对于包装器类型,equals方法并不会进行类型转换。

三.装箱和拆箱相关的面试   

1.下面这段代码的输出结果是什么?

 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);
    System.out.println(i3==i4);
  }
}

 结果:true false;

 为什么会出现这样的结果?输出结果表明i1和i2指向的是同一个对象,而i3和i4指向的是不同的对象。此时只需一看源码便知究竟,下面这段代码是Integer的valueOf方法的具体实现:

 public static Integer valueOf(int i) {
        if(i >= -128 && i <= IntegerCache.high)
            return IntegerCache.cache[i + 128];
        else
           return new Integer(i);
 }

 解析:在通过valueOf方法创建Integer对象的时候,如果数值在[-128,127]之间,便返回指向IntegerCache.cache中已经存在的对象的引用;否则创建一个新的Integer对象。

2.下面这段代码的输出结果是什么?

public class Main {
 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类的valueOf方法会采用与Integer类的valueOf方法不同的实现。很简单:在某个范围内的整型数值的个数是有限的,而浮点数却不是。

注意,Integer、Short、Byte、Character、Long这几个类的valueOf方法的实现是类似的。

Double、Float的valueOf方法的实现是类似的。

 3.谈谈Integer i = new Integer(xxx)和Integer i =xxx;这两种方式的区别。

    1)第一种方式不会触发自动装箱的过程;而第二种方式会触发;

    2)在执行效率和资源占用上的区别。第二种方式的执行效率和资源占用在一般性情况下要优于第一种情况(注意这并不是绝对的)。


One Comment on “Java中的装箱和拆箱

发表评论