创建对象的方式有很多种,最常见的是利用构造器+new
关键字,另外一种常见的方式是用静态工厂方法创建,例如包装类中的value of(int x)
、集合工厂类Collections
中创建各种不同类型集合的方法。
那么,既然通过这两种方式都可以创建类的实例,这两种方式有什么区别呢?
- 静态工厂方法与构造器不同的第一大优势在于,它有名称。
对于这一点,要从静态工厂方式和构造器两个方面分别进行说明。 在利用构造函数创建具有不同状态的对象时,唯一的途径就是通过构造函数参数列表来区分,例如:
class Demo{
private int x = 0;
private int y = 0;
public Demo(int x){
this.x = x;
}
public Demo(int x, int y){
this.x = x;
this.y = y;
}
}
我们可以通过两种构造器来创建不同状态的Demo对象,但这带来的一个问题是:如果没有参考文档,客户端程序员往往不知所云,即可读性差。
与之相反,利用静态工厂方法可以在方法签名的语义层面加以区分,例如:
class Demo{
private int x = 0;
private itn y = 0;
private Demo(int x){
this.x = x;
}
private Demo(int x, int y){
this.x = x;
this.y = y;
}
public static Demo createOneParamDemo(int x){
return new Demo(x);
}
public static Demo createTwoParamDemo(int x, int y){
return new Demo(x, y);
}
}
这样,我们就可以通过慎重的选择方法签名来对创建不同的Demo对象加以区分。
- 静态工厂与构造器不同的第三大优势在于,他们可以返回原返回类型的任何子类型的对象
这样,我们在选择返回对象的类时,就有了更大的灵活性,这种灵活性的一种应用是,API可以返回对象,同时又不会使对象的类变成共有的,以这种方式隐藏实现类会使API变得非常简洁,这种技术适用于基于接口的框架。例如Java Collections Framework。例如:
public static <T> List<T> synchronizedList(List<T> list) {
return (list instanceof RandomAccess ?
new SynchronizedRandomAccessList<>(list) :
new SynchronizedList<>(list));
}
public static <K,V> Map<K,V> synchronizedMap(Map<K,V> m) {
return new SynchronizedMap<>(m);
}
这是一个非常好的例子,如果没有Collections类,我们需要面对的是几十个Java Collection Framework中的构造函数,同时,还有一点不得不提,Collections屏蔽了具体的细节,类的实现者可以对内部进行任意优化或修改而不影响调用者,这是非常重要的。
- 静态工厂方法的第三大优势在于,在创建参数和类型的实例时,它使代码变得更加简洁。
用两个例子来说明这一点区别:
//面对构造器创建对象
Map<String, String> map = new HashMap<String, String>();
//用静态工厂方法
public static <K, V> HashMap<K, V> newInstance(){
return new HashMap<K, V>();
}
Map<String, String> map = HashMap.newInstance();
虽然在实现上看起来代码量更大了, 但是对于调用者而言,更简单了。
有有点就会哟缺点,静态工厂方法有两个明显的缺陷:
类如果不含有共有的或者受保护的构造器,就不能被子类实例化,即不能被继承。
创建对象的静态工厂方法与其他静态方法并无区别。
简而言之,静态工厂方法和公有构造函数都各有用处,需要理解他们各自的长处,单通常来说,优先考虑静态工厂方法。