Java静态工厂方法
2020-02-18 本文已影响0人
风骚无俩
相比构造器,静态工厂方法提供实例的优势:
静态工厂方法有名字
使用静态工厂方法比直接使用等效的构造方法更易阅读理解
BigInteger.java
//生成一个可能的质数
public static BigInteger probablePrime(int bitLength, @NonNull Random random) {
return new BigInteger(bitLength, 100, random);
}
每次调用不必创建新对象
相比构造器,静态工厂返回的对象可以是缓存的
Boolean.java
//预先创建的实例
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);
}
返回的对象可以是返回类型的子类
这个子类可以是非公共的,有利于隐藏实现的细节,比如Collections类里面静态方法返回的对象都是非公共的,但不影响客户端调用。
Collections.java
//到了Java8,接口中可以有静态方法,也就是这样的方法可以放在接口中
public static <K,V> Map<K,V> singletonMap(K key, V value) {
return new SingletonMap<>(key, value);
}
//在接口中这样的静态成员类必须是public,所以如果上面的静态方法在接口中,这样的类必须独立成文件
private static class SingletonMap<K,extends AbstractMap<K, implements Serializable {
private final K k;
private final V v;
SingletonMap(K key, V value) {
k = key;
v = value;
}
...
}
返回的实现类可以根据方法参数改变
由于上面一条带来的优势,静态工厂方法可以根据方法参数返回实现类,可以更加情况增加或减少实现类,而不影响客户端使用
EnumSet.java
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);
}
返回类型的实现类和静态方法不用同步存在
由于静态方法的返回类型可以是接口,所以当方法写好以后,实现类可能还不存在。这是 service provider frameworks的基础,这个框架有三个元素
- a service interface,代表一个实现
- a provider registration API ,服务提供者用来注册服务实例
- a service access API, 客户用来获取服务实列
还有可选的第四个元素 - service provider interface,代表一个服务提供者
以JDBC为例
DriverManager.java
//这个注册方法就是 provider registration API
//Driver是service provider interface,service provider是service的实现
public static synchronized void registerDriver(java.sql.Driver ) throws SQLException {
if(driver != null) {
registeredDrivers.addIfAbsent(new DriverInfo(driver));
} else {
// This is for compatibility with the original DriverManager
throw new NullPointerException();
}
println("registerDriver: " + driver);
}
//方法返回类型Connection 被称为service interface,这个方法就是service access API
public static Connection getConnection(String url) throws SQLException {
java.util.Properties info = new java.util.Properties();
return (getConnection(url, info, Reflection.getCallerClass()));
}
private static Connection getConnection(String url, java.util.Properties info, Class<?> caller) throws SQLException {
...
//遍历注册的service provider,我们期望的是有可能找不到的
for(DriverInfo aDriver : registeredDrivers) {
if(isDriverAllowed(aDriver.driver, callerCL)) {
try {
Connection con = aDriver.driver.connect(url, info);
if (con != null) {
// Success!
println("getConnection returning " + aDriver.driver.getClass().getName());
return (con);
}
} catch (SQLException ex) {
if (reason == null) {
reason = ex;
}
}
}
}
...
}
每个数据库都可以提供自己的驱动,比如:om.mysql.jdbc.Driver,我们在使用它之前需要加载驱动Class.forName(“com.mysql.jdbc.Driver”);当类加载的时候,就通过静态代码块完成service provider的注册。
在java 6中,可以在配置文件中配置service(可能是一个interface或者abstract class)的provider(即service的实现类)。配置路径是:/META-INF/services/下面。不需要使用Class.forName方法手动加载,ServiceLoader为我们提供了框架。
com.mysql.jdbc.Driver.java
static {
try {
java.sql.DriverManager.registerDriver(new Driver());
} catch (SQLException E) {
throw new RuntimeException(“Can’t register driver!”);
}
}
返回与线程相关的实列
结合ThreadLocal,静态工厂方法可以返回与线程相关的独立实列,以下来自Android中的Looper类
Looper.java
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
public static void prepare() {
prepare(true);
}
private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper(quitAllowed));
}
//返回与调用线程关联的实列
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
静态工厂方法的两个缺点
1.只提供静态工厂方法的类,如果没有public或protected构造方法,将不能被继承
2.静态工厂方法不引人注目,容易被忽略,所以使用的时候最好遵守一些命名惯例
- from, 接受单个参数 Date.from(instant);
- of , 接收多个参数 Set<Rank> faceCards =EnumSet.of(JACK, QUEEN, KING);
- valueOf ,比from和of啰嗦一点的备选 BigInteger prime = BigInteger.valueOf(Integer.MAX_VALUE);
- instance or getInstance, 返回参数描述的实列,但返回值可能为空。
StackWalker luke = StackWalker.getInstance(options); - create or newInstance 和上面一样,但这个保证能返回一个新的实例
Object newArray = Array.newInstance(classObject, arrayLen); - getType—和getInstance类似,之所以强调Type,是因为这个方法的返回类型不是调用这个方法的实例类型,比如调用类型File,返回类型FileStore。
FileStore fs = Files.getFileStore(path); - newType—和newInstance类似, BufferedReader br = Files.newBufferedReader(path);
- type—getType and newType的简洁版, List<Complaint> litany = Collections.list(legacyLitany);