第1条 考虑用静态工厂方法代替构造器
使用静态工厂方法有以下优点:
1.当一个类需要多个带有相同签名的构造器时,静态工厂方法的名称能够更好的突出它们之间的区别;
2.不必在每次调用时都创建一个新的对象;3.在创建参数化类型的实例时,使代码更加简洁:
假设HashMap提供了静态工厂方法:
public static <K,V> newInstance(){return new HashMap<K,V>();
}
可以用Map<String ,List<String>> m = HashMap.newInstance();
代替Map<String ,List<String>> m = new HashMap<String ,List<String>>();
第2条 遇到多个构造器参数时要考虑用构建器
当构造参数过多时,有以下几种创建方法:
1.采用重叠构造:写多个构造方法,每个方法的参数数量依次增加,在参数少的构造器中调用参数多的构造器,并用默认值传递多出来的参数。这种方法在参数太多时代码量太大;
2.采用JavaBeans模式,即调用一个无参构造器来创建对象,并用setter方法来设置参数。这种方法的构造过程被分为多次调用,可能使得javaBean处于不一致的状态。
3.Builder模式
示例:
public class NutritionFacts {
private final int servingSize;
private final int serving;
private final int calories;
private final int fat;
private final int sodium;
public static class Builder{
private int servingSize;
private int serving;
private int calories = 0;
private int fat = 0;
private int sodium = 0;
public Builder(int servingSize,int serving){
this.servingSize = servingSize;
this.serving = serving;
}
public Builder calories(int val){
calories = val;
return this;
}
public Builder fat(int val){
fat = val;
return this;
}
public Builder sodium(int val){
sodium = val;
return this;
}
public NutritionFacts build(){
return new NutritionFacts(this);
}
}
private NutritionFacts(Builder builder){
servingSize = builder.servingSize;
serving = builder.serving;
calories = builder.calories;
fat = builder.fat;
sodium = builder.sodium;
}
}
创建方法:
NutritionFacts cocaCola = new Builder(240,8).calories(100).sodium(27).build();
第3条 用私有构造器或者枚举类型强化Singleton属性
Singleton的实现方式主要有三种:
1.公有静态成员是个final域
Public class Elvis{
public static final Elvis INSTANCE = new Elvis();
private Elvis(){ …… }
public void leaveTheBuilding(){ …… }
}
2.公有的成员是个静态工厂方法
Public class Elvis{
private static final Elvis INSTANCE = new Elvis();
private Elvis(){ …… }
public static Elvis getInstance(){return INSTANCE;}
public void leaveTheBuilding(){ …… }
}
以上两种方法中,享有特权的客户端可以借助AccessibleObject.setAccessible方法,通过反射机制调用私有构造器,如果要抵御这种攻击,可以在被要求创建第二个实例的时候抛出异常。
3.包含单个元素的枚举类型,这种方法提供了序列化机制,绝对防止多次实例化,是实现Singleton的最佳方法。
Public enum Elvis{
INSTANCE;
public void leaveTheBuilding(){ …… }
}
第4条 通过私有构造器强化不可实例化的能力
有些类不希望被实例化,如工具类等,实例化对它没有任何意义,若不加以限制,可能会被无意识的实例化,因为在缺少构造器的情况下,编译器会自动提供一个公有的、无参的缺省构造器。
因此我们只要让这个类包含私有构造器,它就不能被实例化了。
第5条 避免创建不必要的对象
1.对于String:
String s = new String(“stringette”); //DO NOT DO THIS!
“stringette”本身就是一个String实例,这样每次执行这条语句就会创建一个新的对象
应该用String s = “stringette”;来代替,这样只要他们包含相同的字符串字面常量,该对象就会被重用。
2.重用那些已知不会被修改的可变对象
示例:
public class Person{
private final Date birthday;
public boolean isBabyBoomer(){
Calendar gmtCal = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
gmtCal.set(1946, Calendar.JANUARY,1,0,0,0);
Data boomStart = gmtCal.getTime();
gmtCal.set(1965, Calendar.JANUARY,1,0,0,0);
Data boomEnd = gmtCal.getTime();
return birthday.compareTo(boomStart)>=0 && birthday.compareTo(boomEnd)<0;
}
}
上述代码中,每次调用isBabyboomer时,都会新建一个Calendar、一个TimeZone和两个Date实例,这是不必要的。优化如下:
public class Person{
private final Date birthday;
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);
Data boomStart = gmtCal.getTime();
gmtCal.set(1965, Calendar.JANUARY,1,0,0,0);
Data boomEnd = gmtCal.getTime();
}
public boolean isBabyBoomer(){
return birthday.compareTo(boomStart)>=0 && birthday.compareTo(boomEnd)<0;
}
}
将这些不变的可变量放到static中,这样只会在类初始化的时候才执行一次。若isBabyBoomer被频繁调用,这种方法性能会显著提高。
3.优先使用基本类型而不是装箱基本类型
public static void main(String[] args){
Long sum = 0L;
for(int i=0;i<Integer.MAX_VALUE;i++){
Sum += i;
}
}
这段计算所有int正值总和的程序,若用Long,相比使用long,运行效率相差非常大。
第6条 消除过期的对象引用
示例:
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);
}
}
}
上面的代码是一个简单的栈实现,其存在内存泄露!当调用pop方法后,栈中存在过期的引用。
如果一个栈先增长,然后收缩,那么从栈中弹出的对象将不会被当做垃圾回收,需要手动回收:
public Object pop(){
if(size == 0){
throw new EmptyStackException();
}
Object result = elements[--size];
elements[size] = null;
return result;
}
第7条 避免使用终结方法
finallizer方法是不能保证会被及时地执行的,注重时间的任务不应该由finallizer方法来完成,一般情况也是不必要使用的,应该尽量避免使用它。