第5条: 避免创建不必要的对象
最好能重用对象而不是在每次需要的时候就创建一个相同功能的新对象。
比如以下的例子:
String s = new String("stringette");
以上的用法是不必要的,因为参数“stringette”本身就是一个String实例。如果这条语句被频繁调用,就会产生很多不必要的实例。
应该改为如下用法:
String s = "stringette";
很多情况下,我们会这么写:
import java.util.Calendar;
import java.util.Date;
import java.util.TimeZone;
public class Person {
private final Date birthDate = new Date();
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(1964, Calendar.JANUARY, 1, 0, 0, 0);
Date boomEnd = gmtCal.getTime();
return birthDate.compareTo(boomStart) >= 0 &&
birthDate.compareTo(boomEnd) < 0;
}
}
以上代码每次调用isBabyBoomer的时候都会创建一个Calendar,一个TimeZone和2个Date实例,其实这样是没有必要的。
可以通过静态初始化器来实现,如下所示:
import java.util.Calendar;
import java.util.Date;
import java.util.TimeZone;
public class Person {
private final Date birthDate = new Date();
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实例一次。
第6条:消除过期的对象引用
import java.util.Arrays;
import java.util.EmptyStackException;
public class Stack {
private Object[] elements;
private int size = 0;
private static final int DEFAULT_INITIAL_CAPACITY = 16;
public Stack(){
elements = new Object[DEFAULT_INITIAL_CAPACITY];
}
public void push(Object e){
ensureCapacity();
elements[size++] = e;
}
public Object pop(){
if(size == 0)
throw new EmptyStackException();
return elements[--size];
}
private void ensureCapacity(){
if(elements.length == size)
elements = Arrays.copyOf(elements, 2*size + 1);
}
}
以上代码有一个“内存泄露”的问题。对栈弹出来的对象,我们叫做过期引用,垃圾回收机制不会对这些对象进行回收,所以这些对象就会被无意的保留下来,这样就导致了“内存泄露”, 极端情况下,会导致“内存溢出”。
以下是处理过期引用的好方法:
public Object pop(){
if(size == 0)
throw new EmptyStackException();
Object result = elements[--size];
elements[size] = null;
return result;
}
清空对象引用应该是一种例外,而不是一种规范行为;
只要类是自己管理内存,程序员就应该警惕内存泄露问题;
内存泄露的另一个常见来源是缓存(可以通过定时清理来清空);
内存泄露的第三个常见来源是监听器和其他回调;
可以通过仔细检查代码,或者借助于Heap剖析工具才能发现内存泄露问题。
第7条:避免使用终结方法
终结方法(finalizer)通常是不可预测的,也是很危险的,一般情况下是不必要的。
Java中,一般用try-finally块来完成类似的工作。
使用终结方法有一个非常严重的性能损失,增加一个终结方法使时间增加很多。
终结方法有2个合法用途,第一种是,当对象的所有者忘记调用前面段落中建议的显式终止方法是,终结方法可以充当“安全网”;第二种合理用途与对象的本地对等体有关。