Java中的每一个类被创建,大部分场景中客户端都需要获取它自身的一个实例然后进行使用,我们最常用的是为它提供一个公有的构造方法,除此之外还可以使用静态工厂方法返回它的一个实例。相较于构造方法,静态工厂方法拥有以下好处:
- 它们拥有不同的自定义名称
- 调用它时无需重复创建新对象
- 它们可以返回类型的任何子类型的对象
- 创建参数化类型实例的时候,它们使代码更加简洁(JDK1.7以后就不算优势了)
静态工厂方法优势一:它们拥有不同的自定义名称
如果构造方法中的参数不能准确的表述会返回什么对象,那么具有适当名称的静态工厂方法则更容易使用,产生的客户端代码也更易于理解。
例如有如下构造方法:
Person(String, int, String)
返回的Person即可能为男人,也可能为女人,当然我们可以创建多个构造方法来解决这个问题,但随着构造方法的增多,使用者很难确定使用哪个构造方法,特别是在参考文档却是的情况下。
如果使用静态工厂方法
Person.male
创建男人对象,使用
Person.female
创建女人对象则更准确。
静态工厂方法优势二:调用时无需重复创建新对象
调用构造方法每次都会创建一个新的实例对象,而有些场景中我们需要控制实例数或者基于性能考虑不能总是重新创建对象,这个时候,可以使用预先初始化实例对象,将其缓存起来,用到的时候直接返回即可。如JDK中的Boolean.valueOf(boolean)方法:
public final class Boolean implements java.io.Serializable, Comparable<Boolean>
{
public static final Boolean TRUE = new Boolean(true);
public static final Boolean FALSE = new Boolean(false);
public static Boolean valueOf(boolean b) {
return (b ? TRUE : FALSE);
}
}
其中
TRUE
和
FALSE
均为预先初始化的实例,静态工厂方法仅进行直接返回已缓存的对象。
静态工厂方法优势三:它们可以返回类型的任何子类型的对象
我们先看一段示例代码:
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>";
public static void registerDefaultProvider(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();
}
}
上面的代码中,接口
Provider
会有多个实现类,而每个实现类中的
newService()
方法均会返回接口
Service
实现类的具体实例化对象,比如在业务场景busA下,需要使用名称为
busAProvider
的
Provider
提供具体服务实例,我们仅需调用
Services.newInstance("busAProvider")
即可获得对应的服务对象实例,而对于外部调用者,只需要调用
Services.newInstance("String)
即可,调用
newInstance()
等同于
Services.newInstance("<def>")
静态工厂方法优势四:创建泛型实例的时候,它们使代码更加简洁(JDK1.7以后就不算优势了)
在JDK1.7之前我们必须这样实例化对象:
Map<String,List<Map<String,Objects>>> map = new HashMap<String,List<Map<String,Objects>>>();
类型参数越来越长,我们实例化对象时也必须指明,假设HashMap提供了一个静态工厂方法:
public static <K, V> HashMap<K, V> newInstance(){
return new HashMap<K, V>();
}
我们仅需这样调用:
Map<String,List<Map<String,Objects>> map = HashMap.newInstance();
使用静态工厂方法后,编译器会自动帮我们找到对应的类型,这个功能叫做
类型推导
,JDK1.7新增了一个钻石操作符
<>
实现了该功能,在1.7中我们可以使用它更简单的完成泛型对象的实例化:
Map<String,List<Map<String,Objects>>> map = new HashMap<>();