第二章 创建和销毁对象
1 考虑使用静态工厂方法替代构造方法
一个类可以提供一个返回类实例的公共的静态工厂方法来替代构造方法,如Boolean#valueOf:
public static Boolean valueOf(boolean b) {
return b ? Boolean.TRUE : Boolean.FALSE;
}
1.1 与构造方法相比的优点
1.静态工厂方法是有名字的,可以更加符合业务逻辑,易于使用
2.静态工厂方法不需要每次调用时都创建一个新对象
如果经常请求等价对象,那么可以提高性能
重复调用返回相同实例让类在任何时候都能对实例保持严格的控制(实例控制类)
3.可以返回此方法返回类型的任何子类型的对象
4.返回对象的类可以根据输入参数的不同而不同,只要为返回类型的子类即可,而构造方法只能返回本类
5.在编写包含该方法的类时,返回的对象的类不需要存在
在服务提供者框架中使用:客户端只需要有父类即可,不需要有具体子类,具体子类由服务提供者返回:
https://www.cnblogs.com/tabCtrlShift/p/9417111.html
1.2 只提供静态工厂方法的主要限制
1.没有公共或受保护构造方法的类不能被子类化,当然,可以使用组合来替代
2.很难找到此方法,对于提供了静态工厂方法而不是构造器的类来说,想要查明如何实例化一个类是十分困难的
2 当构造方法参数过多时使用 builder 模式
静态工厂和构造方法都有一个限制:不能很好地扩展到很多可选参数的情景。
1.这种情景下,可以使用可伸缩构造方法模式:
首先提供一个只有必需参数的构造方法,接着提供增加了一个可选参数的构造函数,然后提供增加了两个可选参数的构造函数,等等,逐个提供可选参数,最终在构造函数中包含所有必需和可选参数
- 评价
比较安全;
但是当有很多参数时,很难编写客户端代码,而且很难读懂它;也很容易不小心写反参数的位置
2.也可以使用JavaBeans模式:
调用一个无参的构造方法来创建对象,然后调用 setter
方法来设置每个必需的参数和可选参数
- 评价
没有伸缩构造方法模式的缺点;虽然有点冗长,但是创建实例很容易,并且易于阅读所生成的代码;
但是由于构造方法被分割成了多次setter,所以在构造过程中 JavaBean 可能处于不一致的状态;
该类没有通过检查构造参数的有效性来进行强制一致性的选项;
在不一致的状态下尝试使用对象可能会导致一些错误,这些错误与平常代码的BUG很是不同,因此很难调试;
一个相关的例子是,JavaBeans 模式排除了让类不可变的可能性(提供了set方法,外界可以调用此方法来修改属性,具体见第 17 ),并且需要程序员增加工作以确保线程安全。
- 缺点的解决方案
通过在对象构建完成时手动”冻结“对象,并且禁止此对象在解冻之前使用,可以减少这些缺点
但是这种方案在实践中很难使用并且很少使用;
而且,在运行时会导致错误,因为编译器无法确保程序员会在使用对象之前调用 freeze
方法。
3.使用Builder模式:
是 Builder 模式的一种形式,将可伸缩构造方法模式的安全性和 JavaBean 模式的可读性结合起来;
客户端(即使用者)不直接构造所需的对象,而是调用一个包含所有必需参数的构造方法 (或静态工厂)得到获得一个 builder 对象;
然后,客户端调用 builder 对象的与 setter
相似方法来设置你想设置的可选参数;
最后,客户端调用builder对象的一个无参的 build
方法来生成所需的对象,该对象的属性通常被制作成不可变的。
Builder 通常是它所构建的类的一个静态成员类
,其 setter 方法返回 builder 本身,这样就可以进行链式调用,从而生成一个流畅的 API。
可以在build方法调用的构造方法中检查包含多个参数的不变性;
如果检查失败,则抛出 IllegalArgumentException
异常(具体见第 72 ),其详细消息指示哪些参数无效(具体见第 75 )。
- 评价
非常灵活,非常适合类层次结构,即使用平行层次的 builder,每个builder嵌套在相应的类中: 抽象类有抽象的 builder;具体的类有具体的 builder;
每个子类 builder 中的 build
方法被声明为返回正确的子类,这样可以进行灵活的多态(协变返回类型技术,即父类引用指向子类对象)
缺点:为了创建对