天天看点

Effective Java——创建和销毁对象

静态工厂方法相对构造器的优势:

  • 静态工厂方法有名称。一个类只有一个带有指定签名的构造器。由于静态工厂方法有名称,所以不受限制。当一个类需要多个带有相同签名的构造器时,就用静态工厂方法代替构造器,并选择合适名称突出区别。
  • 静态工厂方法不必在每次调用的时候都创建一个新对象。静态工厂方法能为重复的调用返回相同的对象,有助于类能控制在某个时刻哪些实例应该存在。[这种类称为实例控制的类]
  • 静态工厂方法可以返回原返回类型的任何子类型的对象。
public interface Service {
    //........
}

public interface Provider {
    Service newService();
}

public class Services {
    private Services(){}
    private static final Map<String, Provider> providers = new ConcurrentHashMap<String, Provider>();
    public static final String DEFAULT_PROVIDER_NAME = "<Jenny>";
    public static void registerDefultProvider(Provider p){
        registerProvider(DEFAULT_PROVIDER_NAME, p);
    }
    public static void registerProvider(String name, Provider p){
        providers.put(name, p);
    }
    public static Service newInstance(){
        return newInstance(DEFAULT_PROVIDER_NAME);
    }
    public static Service newInstance(String name){
        Provider p = providers.get(name);
        if(p == null){
            throw new IllegalArgumentException("no provider registered with name: "+name);
        }
        return p.newService();
    }
}
           
  • 静态工厂方法在创建参数化实例的时候使代码变得更简洁。但是,在调用参数化类的构造器时,即使类型参数很明显,也必须指明。这需要两次提供类型参数:
Map<String,List<String>> m = new HashMap<String,List<String>>();

//eg:HashMap提供了这个静态工厂方法
public static <k, v> HashMap<k, v> newInstance(){
    return new HashMap<k, v>();
}

//就可以通过以下代码替换上面繁琐的声明
Map<String,List<String>> m = HashMap.newInstance();
           

静态工厂方法的缺点:

  • 类如果不含有公有的或受保护的构造器,就不能被子类化。
  • 与其他的静态方法没有区别。

遇到多个构造器参数时,要用构建器

eg.多个构造器

public class NutritrionFats {
    private final int servingSize;
    private final int servings;
    private final int calories;
    private final int fat;
    private final int sodium;

    public NutritrionFats(int servingSize, int servings){
        this(servingSize, servings, );
    }

    public NutritrionFats(int servingSize, int servings, int calories){
        this(servingSize, servings, calories, );
    }

    public NutritrionFats(int servingSize, int servings, int calories, int fat){
        this(servingSize, servings, calories, fat, );
    }

    public NutritrionFats(int servingSize, int servings, int calories, int fat, int sodium){
        this.servingSize = servingSize;
        this.servings = servings;
        this.calories = calories;
        this.fat = fat;
        this.sodium = sodium;
    }
}
           

JavaBean 构建器

public class NutritrionFats {
    private int servingSize = -;
    private int servings = -;
    private int calories = ;
    private int fat = ;
    private int sodium = ;

    public NutritrionFats(){ }

    public void setServingSize(int val){ servingSize = val; }
    public void setServings(int val){ servings = val; }
    public void setCalories(int val){ calories = val; }
    public void setFat(int val){ fat = val; }
    public void setSodium(int val){ sodium = val; }
}
           

这种方式虽然弥补了重叠构造器的不足,但是,JavaBean自身存在不足,因构造过程被分到几个调用中,在构造过程中JavaBean可能处于不一致的状态。类无法通过构造参数的有效性来保证一致性。

下面是Builder模式来完善以上:

public class NutritrionFats {
    private final int servingSize;
    private final int servings;
    private final int calories;
    private final int fat;
    private final int sodium;

    public static class Builder{
        private final int servingSize;
        private final int servings;

        private int calories = ;
        private int fat = ;
        private int sodium = ;
        public Builder(int servingSize, int servings){
            this.servingSize = servingSize;
            this.servings = servings;
        }
        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 NutritrionFats build(){
            return new NutritrionFats(this);
        }
    }

    private NutritrionFats (Builder build){
        servingSize = build.servingSize;
        servings = build.servings;
        calories = build.calories;
        fat = build.fat;
        sodium = build.sodium;
    }
}
           

注意:NutritrionFats是不可变的,所有的默认参数都放在一个地方,builder的setter方法返回builder本身,以便把调用连接起来。以下是调用客户端的代码: