对于Java中的自动装箱,已经是耳熟能详,此处也不多做介绍,本文的重点是,在开发过程中,如何避免自动装箱带来的负面影响。
最直接的方案,自然是能使用基本数据类型的,坚决不使用包装类型,如使用int
而不是用Integer
,使用long
而不是用Long
。
不过总会有一些特殊情况,当该成员表述的值,null
需要被区别对待的时候,就无法不使用包装类型了。如使用boolean
时,true
代表真,false
代表假,null
代表无的时候,就只能使用Boolean
。当然,这种情况可以使用3个整数值代表,使用int
或short
。不过在使用时还是会出现需要区分null
的情况。
我们只需在开发中,避免类似情况,其它情况尽量使用基本数据类型来避免自动装箱带来的问题。
说了那么多,那么自动装箱的负面影响到底是什么呢。
自动装箱实际上就是把数据包装到一个包装类上,那么,自然而然的,需要创建一个对象。如果自动装箱次数太多或者频率太高,大量的创建对象且不能及时回收,会对系统产生什么影响,就很显而易见了。
《Effective Java》中在提到自动装箱时,有这样一个例子:
public class BoxingTest {
public static void main(String[] args) {
long start = System.currentTimeMillis();
Long sum = 0L;
for (int i = 0; i < Integer.MAX_VALUE; i++) {
sum += i;
}
long end = System.currentTimeMillis();
System.out.println(end - start); // 运行多次,平均时间6000ms
}
}
为何这么慢呢,就是因为,每次执行
sum += i
时,sum使用的是Long
接收,需要创建一个对象对结果进行装箱。也就是说,多创建了大约231个对象
如果使用基本数据类型呢:
public class BoxingTest {
public static void main(String[] args) {
/*long start = System.currentTimeMillis();
Long sum = 0L;
for (int i = 0; i < Integer.MAX_VALUE; i++) {
sum += i;
}
long end = System.currentTimeMillis();
System.out.println(end - start); // 运行多次,平均时间6000ms*/
long start = System.currentTimeMillis();
long sum = 0L;
for (int i = 0; i < Integer.MAX_VALUE; i++) {
sum += i;
}
long end = System.currentTimeMillis();
System.out.println(end - start); // 运行多次,平均时间610ms
}
}
没错,整整快了10倍,我们无意识的自动装箱,竟会让程序的性能下降如此之多。
综上,除了一定要使用包装类型的业务场景,其它情况应尽量使用基本数据类型。因为创建对象的代价可能会比较大,这里之所以用可能,是因为现代的JVM实现,在一些小对象的创建和回收上是非常廉价的,所以也没有必要为了不创建对象而降低程序的清晰性、功简洁性和能性。如小对象的对象池就不是很明智的做法。