美文网首页
Java静态工厂方法

Java静态工厂方法

作者: 风骚无俩 | 来源:发表于2020-02-18 14:44 被阅读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);

    相关文章

      网友评论

          本文标题:Java静态工厂方法

          本文链接:https://www.haomeiwen.com/subject/qvegfhtx.html