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的基础,这个框架有三个元素

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.静态工厂方法不引人注目,容易被忽略,所以使用的时候最好遵守一些命名惯例

上一篇下一篇

猜你喜欢

热点阅读