天天看点

创建和销毁对象——用静态工厂方法代替构造器

java 创建类的实例几种方式

  1. 最基础的 new 关键字
  2. 反射
  3. 序列化 - 反序列化
  4. 克隆

本条目中说到的静态工厂方法不是独立的创建对象方式,它内部实际上还是用构造器或者别的方式来创建了对象。

对于类而言,创建对象最传统的方法就是提供一个公有的构造器。

还有一个方法,类可以提供一个公有的静态工厂方法,它只是一个返回类的实例的静态方法。下面是一个来自Boolean (基本类型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 ? Boolean.TRUE : Boolean.FALSE;
} 
           

注意,静态工厂方法与设计模式中的工厂方法模式不同。本条目中所指的静态工厂方法并不直接对应于设计模式(Design Pattern)中的工厂方法。

优势1:静态工厂方法与构造器不同在于它们有名称

如果构造器的参数本身没有明确地描述正被返回的对象,那么具有适当名称的静态工厂会更容易使用,产生的客户端代码也更易于阅读。例如,构造器BigInteger(int ,int ,Random) 返回的BigInteger 可能为素数,如果用名为BigInteger.probablePrime的静态工厂方法来表示,显然更为清楚。(Java 4 版本中增加了这个方法。)

一个类只能有一个带有指定签名的构造器。编程人员通常知道如何避开这一限制:通过提供两个构造器,它们的参数列表只在参数类型的顺序上有所不同。实际上这并不是个好主意。面对这样的API,用户永远也记不住该用哪个构造器,结果常常会调用错误的构造器。并且在读到使用了这些构造器的代码时如果没有参考类的文档,往往不知所云。

由于静态工厂方法有名称,所以它们不受上述限制。当一个类需要多个带有相同签名的构造器时,就用静态工厂方法代替构造器,并且仔细地选择名称以便突出静态工厂方法直接的区别

优势2:不必在每次调用它们的时候都创建一个新对象

构造方法必然是new 一个实例,但静态方法可以根据需要,创建新的实例还是返回已经创建过的实例。这里类似的 Spring 的 @Autowired 注解注入的时候就可以指定scope (prototype,request,session,singleton)

静态工厂方法能够为重复的调用返回相同对象,这样有助于类总能严格控制在某个时刻哪些实例应该存在

优势3:它们可以返回原返回类型的任何子类型的对象

可以返回子类型,而构造器无法灵活处理这些

优势4:所返回的对象的类可以随着每次调用而变化,这取决于静态工厂方法的参数值

只要是已声明的返回类型的子类型,都是允许的。返回对象的类也可能随着发行版本的不同而不同。

EnumSet没有公有的构造器,只有静态工厂方法。在OpenJDK实现中,它们返回两种子类之一的一个实例,具体则取决于底层枚举类型的大小:如果它的元素有64个或更少,就像大多数枚举一样,静态工厂方法就会返回一个RegularEnumSet实例,用单个long进行支持;如果枚举类型有65个或更多,工厂就返回JumboEnumSet实例,用一个long 数组进行支持。

这两个实现类的存在对于客户端来说是不可见的。如果RegularEnumSet不能再给小的枚举类型提供性能优势,就可能从未来的发行版本中将它删除,不会造成任何负面的影响。同样的,如果事实证明对性能有好处,也可能在未来的发行版本中添加第三甚至第四个EnumSet 实现。客户端永远不知道也不关心它们从工厂方法中得到的对象的类,它们只关心它是EnumSet的某个子类。

缺点1:类如果不含公有的或者受保护的构造器,就不能被子类化

影响自身的构造器和子类的行为。对于优点3, 如果需要提供子类,自身的构造器不能是private,例如,要想将Collections中的任何便利的实现类子类化,这是不可能的。但是这样也许会因祸得福,因为它鼓励程序员使用复合,而不是继承,这正是不可变类型所需要的 

缺点2:程序员很难发现它们

在API 文档中,它们没有像构造器那样在API文档中明确标识出来,因此,对于提供了静态工厂方法而不是构造器的类来说,要想查明如何实例化一个类是非常困难的。Javadoc 工具总有一天会注意到静态工厂方法。同时通过在类或者接口注释中关注静态工厂,并遵守标准的命名习惯,也可以弥补这一劣势。下面是静态工厂方法的一些惯用名称。这里只列出了其中的一部分

缺点3:静态方法不能访问实例变量 

补充

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