自动装箱和拆箱就是将基本数据类型和包装类之间进行自动的互相转换。
JDK1.5后,Java引入了自动装箱(autoboxing)/拆箱(unboxing)。
自动装箱:
基本类型的数据处于需要对象的环境中时,会自动转为“对象”。
我们以Integer为例:在JDK1.5以前,这样的代码 Integer i = 5 是错误的,必须要通过Integer i = new Integer(5) 这样的语句来实现基本数据类型转换成包装类的过程;而在JDK1.5以后,Java提供了自动装箱的功能,因此只需Integer i = 5这样的语句就能实现基本数据类型转换成包装类,这是因为JVM为我们执行了Integer i = Integer.valueOf(5)这样的操作,这就是Java的自动装箱。
自动拆箱:
每当需要一个值时,对象会自动转成基本数据类型,没必要再去显式调用intValue()、doubleValue()等转型方法。
如 Integer i = 5;int j = i; 这样的过程就是自动拆箱。
注:自动是假自动,是编译器帮我们做了一些事情。
用一句话总结自动装箱/拆箱:
自动装箱过程是通过调用包装类的valueOf()方法实现的,而自动拆箱过程是通过调用包装类的 xxxValue()方法实现的(xxx代表对应的基本数据类型,如intValue()、doubleValue()等)。
自动装箱与拆箱的功能事实上是编译器来帮的忙,编译器在编译时依据您所编写的语法,决定是否进行装箱或拆箱动作,如下例所示。
示例:自动装箱
Integer i = 100; //自动装箱
//相当于编译器自动为您作以下的语法编译:
Integer i = Integer.valueOf(100); //调用的是valueOf(100),而不是new Integer(100)
示例:自动拆箱
Integer i = 100;
int j = i;//自动拆箱
//相当于编译器自动为您作以下的语法编译:
int j = i.intValue();
null表示i没有指向任何对象的实体,但作为对象名称是合法的(不管这个对象名称存是否指向了某个对象的实体)。由于实际上i并没有指向任何对象的实体,所以也就不可能操作intValue()方法,这样上面的写法在运行时就会出现NullPointerException错误。
示例:自动装箱与拆箱
//测试自动装箱与拆箱,以及空指针的问题
public class Test {
public static void main(String[] args) {
Integer a = 234; //自动装箱,Integer a = Integer.valueOf(234);
int b = a; //自动拆箱,编译器会修改成:int b = a.intValue();
//int d = c; // 此处其实就是:c.intValue(),因此抛空指针异常。
Integer c = null;
if (c != null) {
int d = c; //自动拆箱,调用了:c.intValue();
}
}
}
整型、char类型所对应的包装类,在自动装箱时,对于-128~127之间的值会进行缓存处理,其目的是提高效率。
缓存处理的原理为:如果数据在-128~127这个区间,那么在类加载时就已经为该区间的每个数值创建了对象,并将这256个对象存放到一个名为cache的数组中。每当自动装箱过程发生时(或者手动调用valueOf()时),就会先判断数据是否在该区间,如果在则直接获取数组中对应的包装类对象的引用,如果不在该区间,则会通过new调用包装类的构造方法来创建对象。
以Integer类为例,看一看Java为我们提供的源码,加深对缓存技术的理解,如下例所示
示例:Integer类相关源码如下:
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
这段代码中我们需要解释下面几个问题:
1. IntegerCache类为Integer类的一个静态内部类,仅供Integer类使用。
2. 一般情况下 IntegerCache.low为-128,IntegerCache.high为127,
IntegerCache.cache为内部类的一个静态属性,如下例所示。
示例:IntegerCache类相关源码如下:
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) {
try {
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);
} catch( NumberFormatException nfe) {
// If the property cannot be parsed into an int, ignore it.
}
}
high = h;
cache = new Integer[(high - low) + 1];
int j = low;
for(int k = 0; k < cache.length; k++)
cache[k] = new Integer(j++);
// range [-128, 127] must be interned (JLS7 5.1.7)
assert IntegerCache.high >= 127;
}
private IntegerCache() {}
}
由上面的源码我们可以看到,静态代码块的目的就是初始化数组cache的,这个过程会在类加载时完成。
下面我们做一下代码测试,如下例所示。
示例:测试代码
public class Test3 {
public static void main(String[] args) {
Integer in1 = -128;
Integer in2 = -128;
System.out.println(in1 == in2); //true 因为123在缓存范围内
System.out.println(in1.equals(in2)); //true
Integer in3 = 1234;
Integer in4 = 1234;
System.out.println(in3 == in4); //false 因为1234不在缓存范围内
System.out.println(in3.equals(in4)); //true
}
}
运行结果:
内存分析:
注意
1、JDK1.5以后,增加了自动装箱与拆箱功能,如:
Integer i = 100; int j = new Integer(100);
2、自动装箱调用的是valueOf()方法,而不是new Integer()方法。
3、自动拆箱调用的xxxValue()方法。
4、包装类在自动装箱时为了提高效率,对于-128~127之间的值会进行缓存处理。超过范围后,对象之间不能再使用==进行数值的比较,而是使用equals方法。