Effective Java – 静态方法与构造器

静态工厂方法替代构造器?

传统来讲,为了使客户端能够获取它自身的一个实例,最传统的方法就是提供一个公有的构造器。像下面这样

public class Apple {


public Apple(){}


public static void main(String[] args) {
Apple apple = new Apple();
}
}

还有另外一种其他综合收益属于什么科目方式,为类提供静态工构造器是什么厂方法,它只是返回一个类的静态方法,下面是它系统运维工程师的构造

public static Boolean valueOf(boolean b){
return b ? Boolean.TRUE : Boolean.FALSE;
}

上面代码定义了一个valueof(boolean b)的静态方法,此方法的返回值是一个对 常量 的的引用,为什么说是常量构造器是否可以被重写?跟踪代码进去发现,TRUE是使用 static final修饰的。Boolean.TRUE实际指向的就是一个 Bideao其他应收款olean 类的带有 bool其他综合收益ean类型 构造函数。

    public static final Boolean TRUE = new Boolean(true);

    注意:此静态工厂方法与设计模式中的工厂方法模式不同,本条目中所指的静态构造器方法并不直接系统运维工程师对应设计模式中的工厂方法。

    那么我们为苹果增加一个属性appleSize,并分别提供静态的构造函数bigApple和smallApple,并提供一个方法来判断传进来的值,如果appleS其他综合收益属于什么科目ize > 5的话就是大苹果,其他应收款是什么科目否则都是小苹果,改造后的代码如下

    public class Apple {


    static int appleSize;
    public static final Apple bigApple = new Apple(5);
    public static final Apple smallApple = new Apple(2);


    public Apple(){}
    public Apple(int appleSize){
    this.appleSize = appleSize;
    }
    }


    public class testApple {


    // 判断苹果的大小,大于5的都按5斤算,小于5的都按2斤算
    static Apple judgeAppleSize(int size){
    return size > 5 ? Apple.bigApple : Apple.smallApple;
    }
    public static void main(String[] args) {
    // Apple apple = new Apple();
    judgeAppleSize(6);
    }
    }

    那么,你能否其他和其它的区别根据上述两个代码思考一下静态工厂方法和公有构造器之间孰优孰劣构造器和构造方法的区别呢?

    静态工厂有名称

    众所周知,构造器的声明必须与类名相同,构造方法顾名思义就是构造此类其他应收款是什么科目的方法,也就是通过构造方法能够获得这个类对象的引用,所以构造方法必须与类名相同。不知道你有没有遇见过类似的情况,看下面一个例子

    public BigInteger(int bitLength, int certainty, Random rnd) {
    ...
    prime = (bitLength < SMALL_PRIME_THRESHOLD
    ? smallPrime(bitLength, certainty, rnd)
    : largePrime(bitLength, certainty, rnd));
    }
    public static BigInteger probablePrime(int bitLength, Random rnd) {
    if (bitLength < 2)
    throw new ArithmeticException("bitLength < 2");


    return (bitLength < SMALL_PRIME_THRESHOLD ?
    smallPrime(bitLength, DEFAULT_PRIME_CERTAINTY, rnd) :
    largePrime(bitLength, DEFAULT_PRIME_CERTAINTY, rnd));
    }


    private static BigInteger smallPrime(int bitLength, int certainty, Random rnd) {...}
    private static BigInteger largePrime(int bitLength, int certainty, Random rnd) {...}

    同样是内部调用,静态工厂方法 probabideaslePrime 是你自己定义的名称,你是否从该名称看出来某些关于内部实现的东西呢?是不是就比调用其公有的构造函数要更加明确?

    一个类只能有一个带有指定签名的构造器,如果提供两个构造器,他们只是在identical参数类型的构造器是什么顺序上有所不同,你是不是也会有一头雾水不知道该调用哪个构造器的感觉?事实上这并不是一个好的注意,面对这样的API,用户也记不住调用哪个构造器,结果通常会调用错误的构造器。

    由于静态方法有名称,所以在实现过程中,所以它们不受上述限制,当一个类需要多个带有相同签名的构造器时,就用静态工厂方法替代构造器,并仔细系统/运维的选取静态工厂的名称以便突出其主要功能。

    静态工厂不必重新创建一个对象

    我们都知道,每一次调用一个构造函数都相当于是重新创建了一个该对象的实例,这使得不可变类可以使用预先构建好的示例,或者将构建构造器的作用好的实例缓存起来,重复利用,从而避免创建不必要的对象。Boolean.valueOf(boolean)方法说明了这一点,它从来不用创建对象,这系统运维工资一般多少种方法类似于享元模式,简单介绍一下:构造器

    享元模式

    ​​https://ww其他综合收益属于什么科目w.r系统运维工作内容unoob.com/design-patt其他货币资金包括哪些内容ern/flyweight-patter其他综合收益n.html​​

    言归正传,静态工厂方法不会重新创建其他应收款是什么科目对象,静态工厂方法每次都返回相同的对象,这样有助于控制哪些类的实例应该存在。这种类称为实例受控的类,我们以单例模式为其他货币资金包括哪些内容例,来看一下实例受控的类的主要用法:

    public class Singleton {


    // 懒汉式
    private static Singleton INSTANCE;
    private Singleton(){}
    public static Singleton newInstance(){
    if(INSTANCE == null){
    INSTANCE = new Singleton();
    }
    return INSTANCE;
    }
    }

    这部分代码是一个典型的懒汉式实现,对外部只开放 newInstance 方法,并把构造函数私有化,也就是说你不能通过构造函数 new 出Singleton的实例,必须通过 Singleton.newInstance() 来创建Singl其他业务收入eton的实例,每次判断 INSTAN其他综合收益属于什么科目CE 是否为null,如果是null,则创建并返回 nideaew Singl系统运维工程师eton() 的引用,否则,只是返回之前创建出来的identifySingleide0sgrtsts另类ton 的引其他综合收益用。

    这个Singleton类,就是实例受控的类,你不能无限制的创建Singletion的实例,因为Singleton是identical一种单例实现。当然,这种构造器的作用方式不是线程其他货币资金包括哪些内容安全的,在多个线程并发访问时,你并不能保证单例的有效性,也就是说在多线程环境下你不能保证Singleton只有一个。那么如何保证呢?请往下读,下文会给你答案。

    实例受控的类

    编写实例受控的类有几个原因:其他垃圾

    1. 实例受控的类确保类是一个Singleton

    Singleton是指仅仅被实例化一次的类。那么如何编写一个安全的Singleton呢?我们来对上面的懒汉式进行部分改造。

    public class Singleton {


    // 饿汉式
    private static final Singleton INSTANCE = new Singleton();
    private Singleton(){}
    public static Singleton newInstance(){
    return INSTANCE;
    }


    }

    使用 stidentificationatic final 强制了 INS其他货币资金包括哪些内容TANCE 的引用对象为不可更改的,也就是说,其他和其它的区别你不能再把INS静态工厂方法TANCE对其他和其它的区别象的引用指向其他new Singleton()对象,这种方式就是在类装载的时候就完成实例化。避免了线程同步问题(其他单例的情况我们在后面的章节中讨论)。

    2. 实例受控的类确保类是不能被实例化的

    其实我们上面的代码一直在确保此规定,那就是通过私有化构造函数,确保此类不能被实例化。你也可以通过使用下面这种方式来避免类的实例化

    public class UtilityClass {
    private UtilityClass(){
    throw new AssertionError();
    }
    }

    AssertionEr其他ror()不是必须的,但是它可以避免不小心在类的内部调用构造器。

    3. 实例受控的类确保不构造器是否可以被重写会存在两个相等的实例

    实例受控的类确保不会存在两个相等的实例,当且仅当 a==b时,a.equals(b)才为tr其他垃圾ue,这是享元模式的基础(具体我们在后面的章节中讨论)。

    静态工厂可以返回任何子类型对象

    静态工厂方法与构造器不同的第三大优势在于,它们可以返回原返回类型的任何子identical类型的对象。这样我们就在选择返回对象的类时就有了更大的灵活性。Collections Arrays 工具类保证了这一点

    public static <T> Collection<T> unmodifiableCollection(Collection<? extends T> c) {
    return new UnmodifiableCollection<>(c);
    }


    static class UnmodifiableCollection<E> implements Collection<E>, Serializable {
    ...
    UnmodifiableCollection(Collection<? extends E> c) {
    if (c==null)
    throw new NullPointerException();
    this.c = c;
    }
    ...
    }

    这是Collections.ideasjava 中的代码片段,静态方法idea unmodifiableCollection构造器是否可以被重写 返回一个新的UnmodifiableCollection,ide0sgrtsts另类调用它的静态方法创建UnmodifiableCollection的对象,其他综合收益由于UnmodifiableCollection继承于Collection,也就是说静态方法unmodifiableCollection其idea是什么意思实是返回了一个子类的对象。

    静态工厂返回的类可以动态变化

    静态工厂的第四大优势在于,所返回的对象的类可以ideology随着每次调用而发生变化,这取决于静态工厂ideology方法的参数值。只要是已声明的返回类型的子类型系统/运维,都是允许的。返回对象的类也其他和其它的区别可能随着发行版本的不同而不同。

    EnumSet 没有公有的构造器,只identical有静态工厂方idea法。在OpenJdk实现系统/运维中,它们返回两种子类之一的一个实例,具体则取决于底层枚举类型的大小:如果它的元构造器是什么素有64个或者更少,就像大多数枚举类型一样,静态工厂方其他货币资金法就会返回一其他和其它的区别个RegularEnumSet实例,用单个long其他和其它的区别进行支持;如果枚举类型有65个或者更多元素,工厂就返回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);
    }

    静态工厂返回的类可以不存在

    静态工厂的第五大优势在于,方法返回对象所属的类,在编写包含该静系统运维工作内容态工厂方法类时可以不存在。

    这里直接从 这种静态工厂方法最典型的实现--服务提供者框架 来探讨。

    服务提供者系统运维工作内容框架包含四大组件:(概念不太好理解,可以直接先看下面的例子讲解,然后回过ideas头来再看概念)

    服务接口:这是服务提供者ide0sgrtsts另类要去实现的接口

    服务提供者接口:生系统/运维成服务接口实例的工其他应付款厂对象(就是用来生成服务接口的)(可选)

    提供者注册API:服务者 提供服务者自身的实现

    服务访其他综合收益问API:根据客户端指定的某种条件静态工厂方法去实现对应的服务提供者

    //四大组成之一:服务接口
    public interface LoginService {//这是一个登录服务
    public void login();
    }
    //四大组成之二:服务提供者接口
    public interface Provider {//登录服务的提供者。通俗点说就是:通过这个newLoginService()可以获得一个服务。
    public LoginService newLoginService();
    }
    /**
    * 这是一个服务管理器,里面包含了四大组成中的三和四
    * 解释:通过注册将 服务提供者 加入map,然后通过一个静态工厂方法 getService(String name) 返回不同的服务。
    */
    public class ServiceManager {
    private static final Map<String, Provider> providers = new HashMap<String, Provider>();//map,保存了注册的服务
    private ServiceManager() {
    }
    //四大组成之三:提供者注册API (其实很简单,就是注册一下服务提供者)
    public static void registerProvider(String name, Provider provider) {
    providers.put(name, provider);
    }
    //四大组成之四:服务访问API (客户端只需要传递一个name参数,系统会去匹配服务提供者,然后提供服务) (静态工厂方法)
    public static LoginService getService(String name) {
    Provider provider = providers.get(name);
    if (provider == null) {
    throw new IllegalArgumentException("No provider registered with name=" + name);
    }
    return provider.newLoginService();
    }
    }

    也可以参考这篇文章进一步identification理解:[JAVA 服务提供者框架介绍](https://liwenshui322.iteye.com/blog/1267202)

    静态工厂方法的缺点

    静态工厂方法依赖于构造函数的创建

    上面提到了一些静态其他综合收益工厂其他垃圾方法的优点,那么任何事情都有利弊,静态工厂方法主要缺点在于,类如果不含公有的或者受保护的构造器,就不能被子类化。例如,要想将Collections Framework中任何便利的实现类子类化,这是不可能的。

    静态工厂方法最终也是调用该类的构造方法,如果没有该类的构造方法,静态工厂的方ide0sgrtsts另类法也就没有意义,也就是说,静态工厂方法其实是构造方法的一层封系统运维工作内容装和外观,其实最终还是调用的类的构造方法。

    静态工厂方法很难被发现

    在API文档其他综合收益中,它们没有像构造器那样在API文档中被标明,因此,对于提供了静态工厂方法而不是构造器的类来说,要想查明如何实例化一个类是非常其他应付款困难的。下面提供了一些静态工厂方法的惯用名称。这里只列出来了其中的一小部分

    • from ——— 类型转换方法,系统运维工资一般多少它只有单个参数,返回该类型的一个相系统运维工资一般多少对应的实例,例如:
      Date d = Date.form(instant);
      • of ——— 聚合方法,带有多个参数,返回该类型的一个实例,把他们结合起来,例如:

        Set<Rank> faceCards = EnumSet.of(JACK,QUEEN,KING);
        • valueOf ——— 比from 和 of 更繁琐的一种替代方法,例如:
        BigInteger prime = BigInteger.valueof(Integer.MAX_VALUE);
        • instance 或者 getInstance ———返回构造器是什么的实例是通过方法的(如有)参数来描述的,但是不能说与参数具有相同的值,例如:
        StackWalker luke = StackWalker.getInstance(options);
        • create 或者 newInstance ——— 像instance 或者 getInstance 一样,但create 或者 newInstance 能够确保每次调用都返回一个新的实例,例如:
        Object newArray = Array.newInstance(classObject,arrayLen);
        • getType ——— 像getInstance 一样,但是在工厂方法处于不同的类中的时候使用。Type 表示工厂方法所系统运维工程师返回的对象类型,例如:
        FileStore fs = Files.getFileStore(path);
        • newTyp其他综合收益属于什么科目e ——— 像newInstanfe 一样,但是在工厂方法处于不用的类中的时候使用,Type表示工厂方法返回的对象类型,例如:
        BufferedReader br = Files.newBufferedReader(path);
        • type ——— getType 和 newTideasype 的简版,构造器的作用例如:
          List<Complaint> litany = Collections.list(legacyLitancy);

          简而言之,静态工厂方法和公有构造器都各有用处,我们需要理解它们各自的长处。静态工厂经常更加合适,因此切忌第一反应就是提供公有的构造器,构造器和构造方法的区别而不先考虑静态工厂。

          隆重介绍:

          Java 极客技术公众号,是由一群热爱 Java 开发的技术人组建成立,专注分享原创、高质量的 Java 文章。