为了获取类的一个实例,最常用的方法就是提供一个公有的构造器。还有一种方法:类可以提供一个公有的静态工厂方法,它只是一个返回类的实例的静态方法。
静态工厂方法与设计模式中的工厂模式方法不是一个概念;
静态工厂方法具有名称 由于语言的特性,Java 的构造函数都是跟类名一样的。这导致的一个问题是构造函数的名称不够灵活,经常不能准确地描述返回值,在有多个重载的构造函数时尤甚,如果参数类型、数目又比较相似的话,那更是很容易出错。
一般来说一个类会有多个构造器,他们的参数列表只在参数类型的顺序上有所不同,面对这样的API,用户永远记不住该用哪个构造器,当阅读这样的代码时,如果没有说明文档也会很难理解;
当一个类需要多个构造器时就使用静态工厂方法代替构造器,慎重的选择名称以便突出它们之间的区别;
不必在每次调用静态工厂方法的时候都创建一个新的对象
这使得不可变类可以使用预先构建好的实例,或者将构建好的实例缓存起来进行重复利用,从而避免创建不必要的重复对象。 如果程序经常请求创建相同的对象且创建对象的代价很高则这样可以极大地提升性能;
有助于类严格控制在某个时刻哪些实例应该存在。这些类是实例受控的类:
实例受控使得类可以确保他是一个Singleton;对于不可变的类可以确保不会存在两个相等的实例,即当且仅当ab时才有a.equals(b)为true。因此可以使用代替equals,可以提升性能。静态工厂方法可以返回原返回类型的任何子类型的对象 这条不用多说,设计模式中的基本的原则之一——『里氏替换』原则,就是说子类应该能替换父类。 显然,构造方法只能返回确切的自身类型,而静态工厂方法则能够更加灵活,可以根据需要方便地返回任何它的子类型的实例。
Class Person { public static Person getInstance(){ return new Person(); // 这里可以改为 return new Player() / Cooker() } } Class Player extends Person{ } Class Cooker extends Person{ }比如上面这段代码,Person 类的静态工厂方法可以返回 Person 的实例,也可以根据需要返回它的子类 Player 或者 Cooker。(当然,这只是为了演示,在实际的项目中,一个类是不应该依赖于它的子类的。但如果这里的 getInstance () 方法位于其他的类中,就更具有的实际操作意义了)
在创建带泛型的实例时,能使代码变得简洁 这条主要是针对带泛型类的繁琐声明而说的,需要重复书写两次泛型参数:
Map<String,Date> map = new HashMap<String,Date>();不过自从 java7 开始,这种方式已经被优化过了 —— 对于一个已知类型的变量进行赋值时,由于泛型参数是可以被推导出,所以可以在创建实例时省略掉泛型参数。
Map<String,Date> map = new HashMap<>();所以这个问题实际上已经不存在了。
构造函数虽然也可以有多个,但是由于函数名已经被固定,所以就要求参数必须有差异时(类型、数量或者顺序)才能够重载了。
class Child{ int age = 10; int weight = 30; public Child(int age, int weight) { this.age = age; this.weight = weight; } public Child(int age) { this.age = age; } }Child 类有 age 和 weight 两个属性,如代码所示,它已经有了两个构造函数:Child(int age, int weight) 和 Child(int age),这时候如果我们想再添加一个指定 wegiht 但不关心 age 的构造函数,一般是这样:
public Child( int weight) { this.weight = weight; }但要把这个构造函数添加到 Child 类中,我们都知道是行不通的,因为 java 的函数签名是忽略参数名称的,所以 Child(int age) 跟 Child(int weight) 会冲突。 这时候,静态工厂方法就可以登场了。
class Child{ int age = 10; int weight = 30; public static Child newChild(int age, int weight) { Child child = new Child(); child.weight = weight; child.age = age; return child; } public static Child newChildWithWeight(int weight) { Child child = new Child(); child.weight = weight; return child; } public static Child newChildWithAge(int age){ Child child = new Child(); child.age = age; return child; } }其中的 newChildWithWeight 和 newChildWithAge,就是两个参数类型相同的的方法,但是作用不同,如此,就能够满足上面所说的类似Child(int age) 跟 Child(int weight)同时存在的需求。 (另外,这两个函数名字也是自描述的,相对于一成不变的构造函数更能表达自身的含义,这也是上面所说的第一条优势 —— 『它们有名字』)
参考:《effective Java》 https://www.jianshu.com/p/ceb5ec8f1174