自动装箱和拆箱是什么
在Java中,数据类型可以分为两大类:Primitive Type(原始类型)和Reference Type(引用类型)。基本类型的数值不是对象,不能调用对象的toString()、hashCode()、getClass()、equals()等方法。
自动装箱就是Java自动将原始类型值转换成对应的对象,比如将int值转换成Integer对象,这个过程叫做装箱;反之将Integer对象转换成int值,这个过程叫做拆箱。因为这里的装箱和拆箱是自动进行的非人为转换,所以就称作为自动装箱和拆箱。
原始类型对应的封装类如下表所示。
基本类型 | 大小 | 数值范围 | 默认值 | 包装类型 |
---|---|---|---|---|
boolean | true,false | false | Boolean | |
byte | 8bit | -2^7 ~ 2^7-1 | 0 | Byte |
char | 16bit | \u0000 ~ \uffff | \u0000 | Character |
short | 16bit | -2^15 ~ 2^15-1 | 0 | Short |
int | 32bit | -2^31 ~ 2^31-1 | 0 | Integer |
long | 64bit | -2^63 ~ 2^63-1 | 0 | Long |
float | 32bit | IEEE 754 | 0.0f | Float |
double | 64bit | IEEE 754 | 0.0d | Double |
void | Void |
自动装箱和拆箱的使用场景
自动装箱时编译器调用valueOf将原始类型值转换成对象,而自动拆箱时编译器通过调用类似intValue(),doubleValue()等的方法将对象转换成原始类型值。
/** 赋值时 **/
ArrayList<Integer> intList = new ArrayList<Integer>();
intList.add(1); //autoboxing -> primitive to object
intList.add(2); //autoboxing
ThreadLocal<Integer> intLocal = new ThreadLocal<Integer>();
intLocal.set(3); //autoboxing
int number = intList.get(0); // unboxing
int local = intLocal.get(); // unboxing in Java
/** 方法调用时 **/
public static Integer show(Integer iParam){
System.out.println("autoboxing example - method invocation i: " + iParam);
return iParam;
}
//autoboxing and unboxing in method invocation
show(4); //autoboxing
int result = show(4); //unboxing because return type of method is Integer
自动装箱和拆箱的注意点
1.避免生成无用对象增加GC压力
下例在一个循环中进行自动装箱操作,它创建了多余的对象,影响了程序的性能。
Integer sum = 0;
for(int i = 128; i < 5000; i++){
sum += i;
}
“+”这个操作符不适用于Integer对象,首先sum进行自动拆箱操作,进行数值相加操作,最后发生自动装箱操作转换成Integer对象。其内部变化如下:
temp = sum.intValue() + i;
Integer sum = new Integer(temp);
由于声明的sum为Integer类型,在上面的循环中会创建将近4900个无用的Integer对象,这增加了垃圾回收的工作量,降低了程序的性能。因此在我们编程时需要正确地声明变量类型,避免因为自动装箱和拆箱引起的性能问题。
2.对象初始化
当我们进行一个原始数据值与一个对象的比较时,如果这个对象没有进行初始化或者对象为null,在自动拆箱过程中会抛出NullPointerException异常,代码如下:
private static Integer count;
// NullPointerException on unboxing
if(count <= 0){
System.out.println("Count is not started yet");
}
3.对象缓存
Java默认会对-128到127的Byte、Short、Integer和Long对象进行缓存,当创建的对象在这个数值范围则返回已缓存的对象,否则创建新的对象。Character缓存的范围是0~127,而Float和Double没有自动装箱池。这种设计了享元设计模式,详见享元模式。
然而,我们可以通过getAndRemoveCacheProperties方法来获取或移除JDK对Integer设置的缓存属性,同时可以通过调整虚拟机选项-XX:AutoBoxCacheMax来调整“自动装箱池”的大小 。
4.对象相等比较
Integer integer1 = 100;
Integer integer2 = 100;
System.out.println("integer1==integer2: " + (integer1 == integer2));// true 自动装箱的两个缓存中的 Integer对象的引用比较
System.out.println("integer1.equals(integer2): " + (integer1.equals(integer2)));// true
System.out.println("integer1.compare(integer2): " + integer1.compareTo(integer2));// 0
Integer integer3 = 200;
Integer integer4 = 200;
System.out.println("integer3==integer4: " + (integer3 == integer4));// false 自动装箱的两个new Integer的引用比较
System.out.println("integer3>integer4: " + (integer3 > integer4)); // false 将两个对象拆箱,再比较大小
System.out.println("integer3.equals(integer4): " + (integer3.equals(integer4)));// true
System.out.println("integer3.compare(integer4): " + integer3.compareTo(integer4));// 0
Integer integer5 = new Integer(100);
Integer integer6 = new Integer(100);
System.out.println("integer5==integer6: " + (integer5 == integer6)); // false 两个不同的Integer对象引用的比较
System.out.println("integer5.equals(integer6): " + (integer5.equals(integer6)));// true
System.out.println("integer5.compare(integer6): " + integer5.compareTo(integer6));// 0
int int1 = 100;
System.out.println("integer1==int1: " + (integer1 == int1));// true Integer缓存对象拆箱后与int比较
System.out.println("integer1.equals(int1): " + (integer1.equals(int1)));// true
System.out.println("integer1.compare(int1): " + integer1.compareTo(int1));// 0
int int2 = 200;
System.out.println("integer3==int2: " + (integer3 == int2));// true Integer对象拆箱后与int比较
System.out.println("integer3.equals(int2): " + (integer3.equals(int2)));// true
System.out.println("integer3.compare(int2): " + integer3.compareTo(int2));// 0