Java数据类型(2)------自动封装拆箱

目的:

自动装箱和拆箱从Java 1.5开始引入,目的是将原始类型值转自动地转换成对应的对象,以使用对象的API和引用类型操作。自动装箱与拆箱的机制可以让我们在Java的变量赋值或者是方法调用等情况下使用原始类型或者对象类型更加简单直接。

定义:

自动装箱就是Java自动将原始类型值转换成对应的对象,比如将int的变量转换成Integer对象,这个过程叫做装箱,反之将Integer对象转换成int类型值,这个过程叫做拆箱。因为这里的装箱和拆箱是自动进行的非人为转换,所以就称作为自动装箱和拆箱。原始类型byte,short,char,int,long,float,double和boolean对应的封装类为Byte,Short,Character,Integer,Long,Float,Double,Boolean。

实现:

自动装箱时编译器调用valueOf将原始类型值转换成对象,同时自动拆箱时,编译器通过调用类似intValue(),doubleValue()这类的方法将对象转换成原始类型值。

发生时间:

有一个方法,接受一个对象类型的参数,如果我们传递一个原始类型值,那么Java会自动讲这个原始类型值转换成与之对应的对象。

List<Integer> list = new ArrayList<Integer>();

// 自动装箱

list.add(1);

list.add(2);

// 拆箱

int i = list.get(0);

int ii = list.get(1);

自动装箱的弊端:

自动装箱有一个问题,那就是在一个循环中进行自动装箱操作的情况,如下面的例子就会创建多余的对象,影响程序的性能。

Integer sum = 0;

 for(int i=1000; i<5000; i++){

   sum+=i;

}

上面的代码sum+=i可以看成sum = sum + i,但是==、+、-、*、/这个操作符不适用于Integer对象,首先sum进行自动拆箱操作,进行数值相加操作,最后发生自动装箱操作转换成Integer对象。其过程如下

int temp = sum.intValue() + i;

Integer sum = new Integer(temp);

由于我们这里声明的sum为Integer类型,在上面的循环中会创建将近5000个无用的Integer对象,在这样庞大的循环中,会降低程序的性能并且加重了垃圾回收的工作量。因此,需要注意到这一点,正确地声明变量类型,避免因为自动装箱引起的性能问题.

注意事项:

  自动装箱和拆箱可以让代码简洁,但是,我们在使用的时候应该注意下,否则会发生一些问题。

1.比较

”==“可以用于原始值进行比较,也可以用于对象进行比较,当用于对象与对象之间比较时,比较的不是对象代表的值,而是检查两个对象是否是同一对象,即检查引用地址是否相同。这个比较过程中没有自动装箱发生。进行对象值比较不应该使用”==“,而应该使用对象对应的equals方法。

// 1

int i1=1;

int i2=1;

System.out.println(i2==i1);// true

// 2

Integer I1=1;

System.out.println(I1==i1);// true

Integer I2=1;

System.out.println(I1==I2);// true

// 3

Integer I3 = 128;// 触发自动封装

Integer I4 = 128;

System.out.println(I3==I4);// false

// 4

Integer I5= new Integer(1);// 不触发自动封装

Integer I6= new Integer(1);

System.out.println(I5==I6); // false

值得注意的是第2个小例子的第二个比较,这是一种极端情况。I1和I2的初始化都发生了自动装箱操作。但是处于节省内存的考虑,JVM会缓存-128到127的Integer对象。因为I1和I2实际上是同一个对象。所以使用”==“比较返回true,而第三个小例子使用‘==’返回false。

注:自动封装的函数

public static Integer valueOf(int i) {

 return i >= 128 || i < -128 ? new Integer(i) : SMALL_VALUES[i + 128];

 }

private static final Integer[] SMALL_VALUES = new Integer[256];

2.容易混乱的对象和原始数据值

另一个需要避免的问题就是混乱使用对象和原始数据值,一个具体的例子就是当我们在一个原始数据值与一个对象进行比较或赋值时,如果这个对象没有进行初始化或者为Null,在自动拆箱过程中obj.xxxValue,会抛出NullPointerException,如下面的代码

private static Integer count;

if(count>=0){

System.out.println(11111111111L);

}

3.缓存的对象

在Java中,会对-128到127的Integer对象进行缓存,当创建新的Integer对象时,如果符合这个这个范围,并且已有存在的相同值的对象,则返回这个对象,否则创建新的Integer对象。

4.生成无用对象

因为自动装箱会隐式地创建对象,像前面提到的那样,如果在一个循环体中,会创建无用的中间对象,这样会增加GC压力,拉低程序的性能。所以在写循环时一定要注意代码,避免引入不必要的自动装箱操作。