第一条 用静态工厂方法代替构造器

对于类而言,为了让客户端获取它自身的一个实例,最传统的方法就是提供一个公有的构造器。还有一种方法,也应在在程序员的工具箱中占有一席之地。类可以提供一个公有的 静态工厂方法 ,它只是一个返回类的实例的静态方法。


下面是一个来自 Boolean 的简单实例:这个方法将 boolean基本类型转化成 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);}

注意:

静态工厂方法与设计模式中的工厂方法模式不同,此处所指的静态工厂方法并不直接对应于设计模式中的工厂方法中的工厂方法

静态工厂方法的优势:

①静态工厂方法与构造方法的第一大优势在于:静态工厂方法含有方法名。

通过构造器的参数本身没有确切的描述被返回的对象,那么具有适当名称的静态工厂方法更容易使用,产生的客户端代码也容易阅读。


例如:构造器 BigInteger (int , int , Random)返回的 BigInteger 可能为素数,如果用名为 BigInteger.probablePrime 的静态工厂方法来表示,显然更为清楚。

    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));}

一个类只能有一个带有签名的构造器。我们在编程的时候知道如何避开这一限制:那就是写两个构造器,他们的参数列表只在顺序上有所不同就可以。实际上这并不是一个好主意。面对这样的 API 用户永远也不知道该调用哪一个构造器,结果往往会调用错误的构造器。
由于静态工厂方法有名称,所以他不受上述限制。当一个类需要有多个带有相同签名的构造器时,就用静态工厂方法代替构造器,并且仔细选择名称,以便突出静态工厂方法之间的区别。

②静态工厂方法与构造方法的第二大优势在于:不必在每次调用他们的时候都创建一个新的对象。

这就使得不可变类可以使用预先创建好的实例。或者将构建好的实例缓存起来,进行重复利用,从而避免创建重复的对象。
Boolean.valueOf(boolean) 就说明了这项技术:它从来不创建对象。这种方式类似于享元模式。如果程序经常请求创建相同的对象,并且创建对象的代价很高,则这项技术可以极大的提升性能。


静态工厂方法能够为重复的调用返回相同的对象,这样有助于类总能严格控制在某个时刻那些实例应该存在。这种类被称之为实例受控的类。编写实例受控的类有几个原因。实例受控使得类确保他是一个Singleton,或者是不可实例化的。它还使得不可变的值类可以确保不会存在两个相等的实例,即当且仅当 a==b 时,a.equals(b) 才为true。这是享元模式基础,枚举类型保证了这一点。

③静态工厂方法与构造方法的第三大优势在于:它们可以返回原返回类型的子类型对象。

这样就使得我们在选择返回对象时有了很大的灵活性。这种灵活性的一种应用是,API可以返回对象,同时又不使对象的类变成公有的,以这种方式隐藏实现类会使API变得非常简洁。


在Java 8以前,接口中不能含有静态方法,因此按照惯例,接头 Type 的静态工厂方法被放在一个名为Types的不可实例化的伴生类中。例如:Java Collections Framework 的集合(Collection)接口中有45个工具实现,分别提供了不可修改的集合、同步集合,等等。几乎所有这些实现都通过静态工厂方法在一个不可实例化的类(java.util.Collections)中导出。所有返回对象的类都是非公有的。


现在的Collections Framework API 比导出45个独立公有类的那种实现方式要小得多,每种遍历实现都对应一个类。这不仅仅是指API数量上的减少,也是概念意义上的减少。此外使用这种静态工厂方法时,甚至要求客户端通过接口来引用被返回的对象,而不是通过他的实现类来引用被返回的对象,这是一种良好的习惯。


从Java 8开始,接口中不能含有静态方法这一限制已经成为历史,因此一般没有任何理由给接口提供一个不可实例化的伴生类。但是要注意的是,仍然有必要将这些静态方法背后的大部分实现代码单独放进一个包级私有的类中。这是因为在Java 8中仍要求接口中所有静态成员都必须是公有的。在Java 9中允许接口中有私有的静态方法,但是静态域和静态成员类仍然需要公有的。

④静态工厂方法与构造方法的第四大优势在于:所返回的对象的类可以随着每次调用而发生变化,这取决于静态工厂方法的参数值。

只要是已声明的返回类型的子类型,都是允许的。EnumSet 没有公有的构造器,只有静态工厂方法。它们返回两种子类之一的一个实例,具体取决于底层枚举类型的大小;如果元素小于等于64,返回RegularEnumSet对象,大于64就返回JumboEnumSet对象。

⑤静态工厂方法与构造方法的第五大优势在于:方法返回对象所属的类,在编写包含该静态工厂方法的类时可以不存在。

这种灵活的静态工厂方法构成了服务提供者框架的基础,例如:JDBC API。对于JDBC来说,Connection就是服务接口的一部分,DriverManager.registerDriver就是提供注册的API,DriverManager.getConnection就是服务访问API,Driver就是服务提供者接口。

静态工厂方法的劣势:

①静态工厂方法与构造方法的第一个劣势在于:类如果不含公有的或者受保护的构造器,就不能被子类化。

例如:要想将 Collections Framework 中的任何便利的实现类子类化,这是不可能的。

②静态工厂方法与构造方法的第二个劣势在于:程序员很难发现他们。

在API文档中,它们没有像构造器那样明确的标注出来,因此,对于提供了静态工厂方法而不是构造器的类来说,要查明如何实例化一个类是非常困难的。


下面是静态工厂方法一些惯用名称。(这里只是一小部分)

  • from ---- 类型转化方法,它只传单个参数,返回该类型的一个相对应的实例,例如:
    Date d = Date.from(instant);
  • of -----聚合方法,带有多个参数,返回该类型的一个实例,把他们合并起来,例如:
    Set faceCards = EnumSet.of(JACK, QUEEN, KING);
  • valueOf ----- 比 from 和 of 更繁琐的一种替代方法,例如:
    BigInteger prime = BigInteger.valueOf(Integer.MAX_VALUE);
  • instance 或者 getInstance ---- 返回的实例是通过方法的(如有)参数来描述的,但是不能说预期参数具有同样的值
    StackWalker luck= StackWalker.getInstance(options);

Effective Java相关推荐

  1. 第 3 次读 Effective Java,这 58 个技巧最值!

    点击上方蓝色"方志朋",选择"设为星标" 回复"666"获取独家整理的学习资料! 来源:Dong GuoChao <Effective ...

  2. 读完《Effective Java》后,总结了 50 条开发技巧

    点击上方蓝色"程序猿DD",选择"设为星标" 回复"资源"获取独家整理的学习资料! 作者 | Dong GuoChao 来源 | https ...

  3. Effective Java读书笔记七:泛型(部分章节需要重读)

    第23条:请不要在新代码中使用原生态类型 从java1.5发行版本开始,Java就提供了一种安全的替代方法,称作无限制的通配符类型,如果要使用范型,但是确定或者不关心实际的参数类型,就可以用一个问号代 ...

  4. Effective Java读书笔记六:方法

    第38条:检查参数的有效性 绝大多数方法和构造器对于传递给它们的参数值都会有些限制.比如,索引值必须大于等于0,且不能超过其最大值,对象不能为null等.这样就可以在导致错误的源头将错误捕获,从而避免 ...

  5. Effective Java读书笔记五:异常

    第57条:只针对异常的情况才使用异常 异常是为了在异常情况下使用而设计的,不要将它们用于普通的控制流,也不要编写迫使它们这么做的API. 下面部分来自:异常 如果finally块中出现了异常没有捕获或 ...

  6. Effective Java读书笔记四:通用程序设计

    第45条:将局部变量的作用域最小化 在第一次使用变量时的地方声明: 几乎每个局部变量的声明都应该包含一个初始表达式: 如果在终止循环之后不需要循环变量的内容,for循环优于while循环.(for循环 ...

  7. Effective Java读书笔记三:创建和销毁对象

    第1条:考虑用静态工厂方法代替构造器 对于类而言,为了让客服端获得它的一个实例最常用的的一个方法就是提供一个公有的构造器.还有一种方法,类可以提供一个公有的静态工厂方法(static factory ...

  8. Effective Java读书笔记二:枚举和注解

    第30条:用enum代替int常量 当需要一组固定常量的时候,应该使用enum代替int常量,除了对于手机登资源有限的设备应该酌情考虑enum的性能弱势之外. 第31条:用实例域代替序数 枚举的ord ...

  9. Effective Java读书笔记一:并发

    第66条:同步访问共享的可变数据 关键字synchronized可以保证在同一时刻,只有一个线程可以执行某一个方法,或者某一个代码块. 同步不仅可以阻止一个线程看到对象处于不一致的状态中,它还可以保证 ...

  10. Effective Java:创建和销毁对象

    前言: 读这本书第1条规则的时候就感觉到这是一本很好的书,可以把我们的Java功底提升一个档次,我还是比较推荐的.本博客是针对<Effective Java>这本书第2章所写的一篇读书笔记 ...

最新文章

  1. Tarjan算法 (强联通分量 割点 割边)
  2. 玩转Web之servlet(四)---B/S是如何使用http协议完成通信过程的
  3. 【bzoj4566】[Haoi2016]找相同字符【后缀自动机】
  4. LeetCode 1860. 增长的内存泄露(等差数列)
  5. mysql错误总结-ERROR 1067 (42000): Invalid default value for TIMESTAMP
  6. 一个好用的PHP验证码类
  7. Atitit 提升开发效率使用内嵌Tomcat 内嵌webserver 于单元测试
  8. Java、JSP宾馆入住管理系统的设计与实现
  9. 浦发银行计算机抓紧用面试题目,浦发银行面试问题
  10. 数据可视化(一):matplotlib
  11. git rebase和git merge使用方法详解
  12. INE上线BiKi,开启“充值领空投+最少买入,也拿万元锦鲤”活动
  13. eve-ng学习笔记
  14. 雕刻效果的实现【OpenCV+QT】
  15. Unity3D游戏开发之快速打造流行的关卡系统
  16. 数值计算练习 LU分解(杜里特尔和克洛特分解)
  17. vue+echarts5 实现中国地图
  18. jar包的类什么时候需要@Bean引入
  19. 八种常用激光雷达和视觉SLAM算法的评估与比较
  20. 企业互联网化、企业社会化、企业社交化

热门文章

  1. 常见低压电器原理及电气符号(接触器、继电器、熔断器、断路器)基本原理及电气间隙与爬电距离
  2. 什么是前台?什么是中台?什么是后台?
  3. 十大算法之迪杰斯特拉算法
  4. 对角占优矩阵(Diagonally-dominant Matrix)
  5. 计算机切换用户快捷键,Win10系统中快速切换用户有哪些快捷小技巧
  6. 在服务器上如何打开mdf文件,在没sql server数据库状况下怎么打开.mdf文件
  7. ecos kernel 分析 转自黑嘴公 PiPi Cat
  8. 手机php文件怎么修改,安卓手机上pdf文件怎么修改
  9. 常用的20个js简洁代码
  10. Niushop官网出新版 由内而外 玩转简约时尚风