1 什么是自动装箱?什么是自动拆箱?
自动装箱和自动拆箱的示例:
public class Test {
public static void main(String[] args) {
ArrayList<Integer> intList = new ArrayList<Integer>();
intList.add(100);
intList.add(150);
intList.add(200);
int a = intList.get(0);
int b = intList.get(1);
int c = intList.get(2);
System.out.println(a + ", " + b + ", " + c);
}
}
intList列表中只能放Integer类型的对象,而代码中把int基本数据类型放到intList列表中,那么实际上编译器会把int基本数据类型转换城Integer类型(包装类)的对象,那么编译器的这个操作过程就是装箱的过程;而从intList列表中拿取的也是Integer类型的对象,而代码中把Integer类型的对象赋值给int基本数据类型,那么编译器会把Integer类型的对象转换成int基本数据类型,那么编译器的这个操作过程就是拆箱的过程;另外,由于装箱和拆箱都是编译器自动完成,不需要在代码中显式的说明,故称为自动装箱和自动拆箱。基本数据类型byte,short,char,int,long,float,double和boolean对应的包装类为Byte,Short,Character,Integer,Long,Float,Double和Boolean。
2 如何实现自动装箱和自动拆箱?
自动装箱和自动拆箱由编译器自动完成。当编译器检测到需要把基本类型转换成包装类型时,编译器自动调用包装类型的valueOf()方法实现自动装箱;当编译器检测到需要把包装类型转换成基本类型时,编译器自动调用包装类型的xxValue()方法实现自动拆箱。
八大基本数据类型自动装箱的原理代码:
// Byte
public static Byte valueOf(byte b) {
final int offset = 128;
return ByteCache.cache[(int)b + offset];
}
// Short
public static Short valueOf(short s) {
final int offset = 128;
int sAsInt = s;
if (sAsInt >= -128 && sAsInt <= 127) { // must cache
return ShortCache.cache[sAsInt + offset];
}
return new Short(s);
}
// Character
public static Character valueOf(char c) {
if (c <= 127) { // must cache
return CharacterCache.cache[(int)c];
}
return new Character(c);
}
// Ingeger
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
// Long
public static Long valueOf(long l) {
final int offset = 128;
if (l >= -128 && l <= 127) { // will cache
return LongCache.cache[(int)l + offset];
}
return new Long(l);
}
// Double
public static Double valueOf(double d) {
return new Double(d);
}
// Float
public static Float valueOf(float f) {
return new Float(f);
}
// Boolean
public static Boolean valueOf(boolean b) {
return (b ? TRUE : FALSE);
}
从装箱的源代码可知,当基本类型为byte,short,char,int和long类型时,首先根据赋值的数值大小判断是否在缓存区,否则直接new一个对应的包装类型对象;注意只有double和float会直接new一个包装类。(因为基本数据类型byte,char和int用的较多,所以Java在包装类初始化的时候在内存中缓存一定范围的值,避免生成大量的对象。缓存范围为[-128,127],正好是short基本类型的范围)。
八大基本数据类型自动拆箱的原理代码:
public xxx xxxValue() {
return value;
}
3 自动装箱和自动拆箱有什么作用?有什么弊端?
JDK1.5之前,编译器还没有实现自动装箱和自动拆箱,需要显式的编写装箱和拆箱的过程;如下面的代码:
intList.add(Integer.valueOf(100));
intList.add(Integer.valueOf(150));
intList.add(Integer.valueOf(200));
int _a = intList.get(0).intValue();
int _b = intList.get(1).intValue();
int _c = intList.get(2).intValue();
可以看出代码很繁杂,JDK1.5之后的valueOf()和intValue()由编译器自动完成,可以简化代码。
自动装箱和自动拆箱给我们带来便利的同时,也带来了弊端,主要体现以下几点:
- 包装类之间的比较;
public static void method2() {
Integer a = 100;
int _a = 100;
Integer _a1 = new Integer(100);
System.out.println("a == _a : " + (a == _a)); // true
System.out.println("a == _a1 : " + (a == _a1)); // false
System.out.println("_a == _a1 : " + (_a == _a1)); //true
Integer b = 200;
int _b = 200;
Integer _b1 = new Integer(200);
System.out.println("b == _b : " + (b == _b)); // false
System.out.println("b == _b1 : " + (b == _b1)); // false
System.out.println("_b == _b1 : " + (_b == _b1)); //false
}
==可以用于对象与对象之间的比较,比较的是对象的内存地址是否相等,即是否是同一个对象,比较的过程没有自动拆箱;也可以用于基本数据类型之间的比较,比较的是字面值;所以,对于对象之间的比较应该用equals()方法。
- 稍不留神,容易创建额外对象
Integer result = 0;
for (int i = 127; i < 1127; i++) {
result += i;
}
上面的result += i 实际会先进行自动拆箱操作,然后进行自动装箱操作。即
result = result.intValue() + i;
result = result.valueOf(result);
自动装箱实际会创建1000各无用的Integer对象,会给内存造成极大的浪费;所以编写代码中避免使用不必要的自动装箱。