第1条:考虑用静态工厂方法代替构造器
为了获得一个类的实例,有两种办法1.类提供一个公有的构造器 2.类提供一个公有的静态工厂方法。
静态工厂方法的优势:
1.有名称。
慎重地选择方法名称能突出多个构造器的区别,例如使用BigInteger(int, int, Random)构造器,返回的BigInteger可能为素数,如果用
BigInteger.probalePrime(int, Random)静态工厂方法,显得更为清楚。
2.不必在每次调用的时候都创建一个新的对象。
Boolean类的代码中有public static final Boolean TRUE = new Boolean(true) 这样一个TURE常量,它在类加载的时候就把这个常量初始化,在valueOf方法中每次返回
的都是这个常量。
public static Boolean valueOf(boolean b) { return (b ? TRUE : FALSE); }
3.可以返回原返回类型的任何子类型的对象。(多态)
java.util.EnumSet没有公有构造器,只有静态工厂方法,返回两种实现类之一,具体取决于底层枚举类型的大小,如果元素有64个或更少,返回一个RegualrEumSet实例,用单个long支持,如果多于64个,则返回JumboEnumSet实例,用long数组进行支持。
public static <E extends Enum<E>> EnumSet<E> noneOf(Class<E> elementType) { Enum<?>[] universe = getUniverse(elementType); if (universe == null) throw new ClassCastException(elementType + " not an enum"); if (universe.length <= 64) return new RegularEnumSet<>(elementType, universe); else return new JumboEnumSet<>(elementType, universe); }
//RegularEnumSet用单个long支持 private long elements = 0L;
//JumboEnumSet用long数组支持 private long elements[];
RegualrEumSet和JumboEnumSet这两个实现类对客户来说不可见,客户只关心的是它是某个EnumSet的某个子类,如果RegularEnumSet不能给小的枚举类型提供性能优势,就有可能从未来的发行版中将它删除,不会对客户造成任何影响,如果在未来的发行版中要添加第三第四个甚至更多EnumSet实现,对客户也没有任何影响。
一个服务提供者的框架:
//服务接口,由服务提供者实现 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 = "<def>"; //服务提供者注册API,系统用来注册实现 public static void registerDefaultProvider(Provider p) { registerProvider(DEFAULT_PROVIDER_NAME, p); } public static void registerProvider(String name, Provider p) { providers.put(name, p); } //服务访问API,客户用来获取服务实例 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(); } }
结合JDBC的数据库连接服务理解:Connection是服务接口,DriverManager.registerDriver是提供者注册API,DriverManager.getConnection是服务访问API,Driver就是服务提供者接口。
这样的实现使得Services类(在JDBC中,即是DriverManager类)的代码几乎不需要修改了(就算是修改也不会把框架改了,只会优化,对外的接口是不会变的),现在来了一个新的数据库厂商,该厂商只需要提供实现了Provider接口的服务提供者,实现Service接口的服务,就可以了,不会对客户造成任何影响,因为客户根本不知道多了一个新的数据库。
4.在创建参数化类型实例的时候,使代码变得更加简洁。
Map<String, List<String>> m = new HashMap<String, List<String>>()
调用参数化类型时,需要两次提供类型参数<String, List<String>>
假如HashMap提供了这样一个静态工厂:
public static <K, V> HashMap<K, V> newInstance() { return new HashMap<K, V>(); }
就能使用
Map<String, List<String>> m = HashMap.newInstance();
代替原来的冗长代码。
静态工厂方法的主要缺点:
1.类的构造器如果是私有的,就不能被子类化。
2.它们与其他静态方法实际上没有任何区别。
就像上面提到的EnumSet类,它没有提供公有的构造器,所以无法这样实例化EnumSet s = new EnumSet(),在Eclipse会提示你这个类不没通过构造器实例化,除此之外没有任何有用的消息了,它不会告诉你使用noneof、allof这样的静态工厂方法来实例化该对象。