详解 Java 的自动装箱与拆箱

1、什么是自动装箱?自动拆箱?

  • 从 Java SE5 开始就提供了自动装箱的特性,简单一点说:
    装箱就是 自动将基本数据类型转换为包装器类型;
    拆箱就是 自动将包装器类型转换为基本数据类型。java

    //自动装箱
    Integer i = 10;  
    
    //自动拆箱
    int n = i;   
    复制代码
  • 下表是基本数据类型对应的包装器类型:面试

基本数据类型 包装器类型
byte(1字节) Byte
short(2字节) Short
int(4字节) Integer
long(8字节) Long
float(4字节) Float
double(8字节) Double
char(2字节) Character
boolean(4字节) Boolean

2、装箱和拆箱如何实现

以 Integer 类为例,下面看一段代码:数组

public class Test {
	public static void main(String[] args) {
		Integer i = 10;
		int j = i;
	}
}
复制代码

先编译:javac Test.java
再反编译:javap -c Test.class
字节码输出以下: 缓存

能够看出:

在装箱的时候自动调用 Integer 的 valueOf (int) 方法;  
拆箱的时候自动调用 Integer 的 intValue 方法。  
所以能够用一句话总结装箱和拆箱的实现过程:  
装箱过程是经过调用包装器的 valueOf 方法实现;  
而拆箱过程是经过调用包装器的 xxxValue 方法(xxx 表明对应的基本数据类型)。
复制代码

3、面试中的相关问题

下面列举一些常见的与装箱 / 拆箱有关的面试题。bash

1. Integer

public class Main {
    public static void main(String[] args) {
        Integer i1 = 100;
        Integer i2 = 100;
        Integer i3 = 128;
        Integer i4 = 128;
         
        System.out.println(i1==i2); // true
        System.out.println(i3==i4); // false
    }
}
复制代码

输出结果代表 i1 和 i2 指向的是同一个对象,而 i3 和 i4 指向的是不一样的对象。
源码分析一下 Integer 的 valueOf 方法的具体实现:源码分析

public static Integer valueOf(int i) {
        assert IntegerCache.high >= 127;
        if (i >= IntegerCache.low && i <= IntegerCache.high)
            return IntegerCache.cache[i + (-IntegerCache.low)];
        return new Integer(i);
}
复制代码

其中,IntegerCache 的源码实现以下:ui

private static class IntegerCache {
        static final int low = -128;
        static final int high;
        static final Integer cache[];

        static {
            // high value may be configured by property
            int h = 127;
            String integerCacheHighPropValue =
                sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
            if (integerCacheHighPropValue != null) {
                int i = parseInt(integerCacheHighPropValue);
                i = Math.max(i, 127);
                // Maximum array size is Integer.MAX_VALUE
                h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
            }
            high = h;

            cache = new Integer[(high - low) + 1];
            int j = low;
            for(int k = 0; k < cache.length; k++)
                cache[k] = new Integer(j++);
        }

        private IntegerCache() {}
}
复制代码

从这 2 段代码能够看出:spa

  • 在经过 valueOf 方法建立 Integer 对象的时候,若是数值在 [-128,127] 之间,便返回指向静态缓存 IntegerCache.cache 的数组中已经存在的对象的引用;不然建立一个新的 Integer 对象。3d

  • 上面的代码中 i1 和 i2 的数值为 100,所以会直接从 cache 中取已经存在的对象,因此 i1 和 i2 指向的是同一个对象,而 i3 和 i4 则是分别指向不一样的对象。code

2. Double

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); // false
        System.out.println(i3==i4); // false
    }
}
复制代码

其中 Double 的 valueOf 源码以下:

public static Double valueOf(double d) {
        return new Double(d);
}
复制代码

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

3. Boolean

public class Main {
    public static void main(String[] args) {
        Boolean i1 = false;
        Boolean i2 = false;
        Boolean i3 = true;
        Boolean i4 = true;
         
        System.out.println(i1==i2); // true
        System.out.println(i3==i4); // true
    }
}
复制代码

Boolean 类的源码的 valueOf 实现:

public static Boolean valueOf(boolean b) {
        return (b ? TRUE : FALSE);
}
复制代码
public static final Boolean TRUE = new Boolean(true);
public static final Boolean FALSE = new Boolean(false);
复制代码

从这2段代码能够看到:

  • 返回的都是 true,也就是它们执行 valueOf 返回的都是相同的对象。
  • 它并无建立对象,而是返回在内部已经提早建立好两个静态全局对象,由于它只有两种状况,这样也是为了不重复建立太多的对象。

4. == 和 equals

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 先拆箱再装箱,比较对象里面的value值
        System.out.println(g==(a+b)); // true
        System.out.println(g.equals(a+b)); // false 类型不一样
        System.out.println(g.equals(a+h)); // true 拆箱、向上转型、装箱、比较value值
    }
}

复制代码

注意:

  • 当一个基础数据类型与封装类进行 ==、+、-、*、/ 运算时,会将封装类进行拆箱,对基础数据类型进行运算;
  • 当 "==" 运算符的两个操做数都是 包装器类型的引用,则是比较指向的是不是同一个对象;
  • 而若是其中有一个操做数是表达式(即包含算术运算)则比较的是数值(即会触发自动拆箱的过程);
  • equals 它必须知足两个条件才为 true:
    1) 类型相同
    2) 内容相同

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

主要有如下这两点区别: 1) 第一种方式不会触发自动装箱的过程;而第二种方式会触发; 2) 在执行效率和资源占用上的区别。第二种方式的执行效率和资源占用在通常性状况下要优于第一种状况(注意这并非绝对的)。

相关文章
相关标签/搜索