什么是静态工厂方法
- 在Java中,我们得到类的实例的最常用的方法是使用 new 关键字来调用类的构造方法。
- 每使用一个new语句,JVM就会在内存中的堆中开辟一个空间,然后调用构造方法来初始化成员变量,最后把对象的引用返回给调用方。
静态工厂方法的优点
1.静态工厂方法可以有名称。
如果构造器的参数本身没有确切地描述正被返回的对象,那么有适当名称的静态工厂方法会更容易使用,产生的代码也更易于使用。比如,构造函数BigInteger(int,int,Random)返回的BigInteger可能是素数,但如果使用名为BigInteger.probablePrime的静态工厂方法来表示,这样会更加清楚。
一个类只能有一个带有指定方法签名的构造函数,在我们想要避开这个限制的时候,我们提供两个构造函数,他们参数列表只有顺序不同。但这个其实并不是个好方法,因为我们经常会忘记这个参数的顺序,调用了错误的构造函数。但是我们可以通过使用静态工厂方法来解决这个问题,如果一个类需要多个带有相同签名的构造函数时,我们可以用不同名称的静态工厂方法来替代构造函数,仔细地选择名称来突出静态工厂方法的区别。
2.静态工厂方法不是每次调用,都要创建新的对象。
不可变类是指实例不能被修改的类。每个实例中的所有信息都必须在创建时就提供,而且在对象的整个生命周期中还要保持不变。 比如有String,基本类型的包装类,BigInteger,BigDecimal。
我们使用静态工厂方法,可以让这些不可变类使用预先构建好的实例,或者将构建好的实例缓存起来,进行重复利用,从而避免创建不必要的重复对象。如果程序经常请求创建相同的对象,并且创建对象的代价很高,那么这个技术就可以极大地提高性能。
静态工厂方法能够为重复的调用返回相同对象,这样有助于类总能严格在某个时刻哪些实例应该存在。这样的类叫做实例受控的类(instance-controlled)。
编写实例受控的类有几个原因:
- 实例受控使得类可以确保自己是一个单例类(Singleton)或者是不可实例化的。
- 使得不可变的类可以确保不会存在两个相等的实例。就是当且仅当a == b时,a.equals(b)才为true。
3.静态方法可以返回原类型的任何子类型的对象,让我们在选择返回对象的类时就有了更大的灵活性。
里氏替换原则中说, 任何基类可以出现的地方,子类一定可以出现。只有当衍生类可以替换掉基类,软件单位的功能不受到影响时,基类才能真正被复用,而衍生类也能够在基类的基础上增加新的行为。
参考别人对这个的解释
构造方法只能返回确切的自身类型,也就是说什么方法的构造函数会返回什么类型的对象,但是静态方法可以灵活的控制返回类型,可以根据需要来返回需要的子类的实例。
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 () 方法位于其他的类中,就更具有的实际操作意义了)
4.静态工厂所返回的类可以随着每次调用而发生变化,这取决于静态工厂方法的参数值。
举的例子是EnumSet,小于64个元素会返回枚举类型,返回RegularEnumSet实例,用单个long进行支持;有65个或者更多的元素,工厂就返回JumboEnumSet实例,用一个long数组进行支持。
5.静态工厂方法返回的对象所属的类,在编写包含该静态工厂方法的类时可以不存在。
静态工厂方法的缺点
1.类如果不含public或者protected的构造函数,就不能被子类化。
2.静态方法很难被发现。
下面是一些被经常使用的静态工厂方法的名称:
1.from
//from 类型转换
//单个参数,返回这个类型中的一个相对应的实例
Date d = Date.from(instant);
2.of
//of 聚合方法
//多个参数,返回该类型的一个实例,把他们合并起来
Set<Rank> faceCards = EnumSet.of(JACK,QUEEN,KING);
3.valueOf
//valueOf
BigInteger prime = BigInteger.valueOf(Integer.MAX_VALUE);
4.instance或者getInstance
//instance或者getInstance
//返回的实例是通过方法的参数来描述的
StackWalker luke = StackWalker.getInstance(options);
5.create或者newInstance
//create或者newInstance
//像instance或者getInstance方法一样,但这个方法能够确保每次调用都返回一个新的实例。
Object newArray = Array.newInstance(classObject,arrayLen);
6.getType
//getType Type表示工厂方法返回的对象类型
//像getInstance一样,在工厂方法处于不同的类中的时候使用
FileStore fs = Files.getFileStore(path);
7.newType
//newType Type表示工厂方法返回的对象类型
//像newInstance一样,在工厂方法处于不同的类中的时候使用
BufferedReader br = Files.newBufferedReader(path);
8.type
//type
//getType和newType的简版
List<Complaint> litany = Collections.list(legacyLitany);
静态方法和构造函数都各有用处,要理解各自的长处。不要第一反应就直接调用构造函数,要多加考虑再进行使用。