一般来说,最好能重用对象而不是在每次需要的时候就创建一个相同功能的新对象。重用方式既快速,又流行。如果对象是不可变的,它就始终可以被重用。
最为一个极端的反面例子,考虑下面的语句:
String s = new String("string");// Don't do this!
该语句每次被执行的时候都创建一个新的String实例,但是这些创建对象的动作全都是不必要的。传递给String构造器的参数("string")本身就是一个String实例,功能方面等同于构造器创建的所有对象。如果这种用法是在一个循环中,或者是在一个被频繁调用的方法中,就会创建出成千上万不必要的String实例。改进后:String s="stirng";
这个版本只用了一个Stirng实例,而且每次执行的时候都创建一个新的实例。而且,它可以保证,对于所有的同一台虚拟机中运行的代码,只要它们包含相同的字符串字面常量,该对象就会被重用。
例如:静态工厂方法Boolean.valueOf(String)几乎总是优先于构造器Boolean(String)。构造器在每次被调用的时候都会创建一个新的对象,而静态工厂方法则从来不要求这样做,实际上也不会这样做。
public class Person{
private final Date birthDate;
public boolean isBabyBoomer(){
Calendar gmtCal = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
gmtCal.set(1946,Calendar.JANUARY,1,0,0,0);
Date boomStart = gmtCal.getTime();
gmtCal.set(1965,Calendar.JANUARY,1,0,0,0);
Date boomEnd = gmtCal.getTime();
return birthDate.compareTo(boomStart ) >=0 &&birthDate.compareTo(boomEnd ) <0;
}
}
看看这段代码有什么不妥,isBabyBoomer每次被调用的时候,都会新建一个Calendar,一个TimeZone和两个Date实例,这是不必要的,下面的版本用一个静态的初始化器,避免了这种效率低下的情况:
class Person{
private final Date birthDate;
private static final Date BOOM_START;
private static final Date BOOM_END;
static{
Calendar gmtCal=Calendar.getInstance(TimeZone.getTimeZone("GMT"));
gmtCal.set(1946,Calendar.JANUARY,1,0,0,0);
BOOM_START = gmtCal.getTime();
gmtCal.set(1965,Calendar.JANUARY,1,0,0,0);
BOOM_END= gmtCal.getTime();
}
public boolean isBabyBoomer(){
return birthDate.compareTo(BOOM_START;)>=0 &&birthDate.compareTo(BOOM_END)<0;
}
}
改进后的Person类只在初始化的时候创建Calendar,TimeZone和Date实例一次,而不是在每次调用isBodyBoomer的时候都创建这些实例。如果isBodyBoomer方法被频繁的调用,这种方法将会显著提高性能,代码的含义也更加清晰,但是这种优化并不总是那么明显,因为Calendar实例的创建代价特别昂贵。
如果改进后的Person类被初始化了,他的isBodyBoomer方法却永远不会被调用,那就没有必要初始化BOOM_START,BOOM_END域。通过延迟加载,即可对这些域的初始化延迟到isBodyBoomer方法第一次被调用的时候进行,则有可能消除这些不必要的初始化工作,但是不建议这样做。正如延迟初始化中常见的情况,这样会使方法的实现更加复杂,从而无法将性能显著提高到超过已经达到的水平。
再如:自动装箱问题
public static void main(String[] args){
Long sum=0L;
for(long i = 0; i<Integer.MAX_VALUE;i++){
sum+=i;
}
System.out.println(sum);
}
这段程序算出的答案是正确的,但是比实际情况要慢些,因为之前声明的sum是Long型,而不是long型,这样在运行的时候就会创建出2的31次方个实例,会大大消减效率,结论很明显:要优先使用基本类型而不是装箱基本类型,要当心无意识的自动装箱。
不要错误的以为本文介绍的内容暗示“创建对象的代价非常昂贵,我们应该尽可能地避免创建对象”。相反,由于小对象的构造器只做很少量的显示工作,所以,小对象的创建和回收动作是非常廉价的,特别是在现在的JVM实现上更是如此。通过创建附加的对象,提升程序的清晰性,简洁性和功能性,这通常是件好事。