对于一个类而言,为了让客户端获取到它自身的一个实例,有两种方法:
- 提供一个公有的构造器
- 提供一个公有的静态工厂方法
静态工厂方法返回类的实例,以Boolean类为例:
public static Boolean valueOf(boolean b) {
return (b ? TRUE : FALSE);
}
该方法接收boolean基本类型,返回Boolean对象引用
静态工厂与构造器相比的优势:
1. 静态工厂方法有名称,可能能够确切描述正被返回的对象
例如BigInteger的probablePrime方法:
Returns a positive BigInteger that is probably prime, with the specified bitLength. The probability that a BigInteger returned by this method is composite does not exceed
.
public static BigInteger probablePrime(int bitLength, Random rnd) {
if (bitLength < 2)
throw new ArithmeticException("bitLength < 2");
return (bitLength < SMALL_PRIME_THRESHOLD ?
smallPrime(bitLength, DEFAULT_PRIME_CERTAINTY, rnd) :
largePrime(bitLength, DEFAULT_PRIME_CERTAINTY, rnd));
}
返回一个具有指定位长度的正的BigInteger, 它可能是素数,为合数的可能性不超过,"probablePrime"直接指明返回的实例的类型(大概率是素数)
2. 不必每次调用的时候都创建一个新对象,可以使用预先构建好的实例,或者复用构建好的实例,能够:①避免创建不必要的重复对象;②有助于类严格控制在某个时刻有哪些实例应该存在
以Runtime类为例,通过静态工厂方法实现为重复调用返回相同实例对象,保证唯一性
public class Runtime {
private static final Runtime currentRuntime = new Runtime();
public static Runtime getRuntime() {
return currentRuntime;
}
private Runtime() {}
3. 可以返回原返回类型的任何子类型的对象, 能够:①不会使对象对应的子类型变成公有的,从而隐藏实现类;②不必知道实现类的存在,不需要多写一个外部类,减少API数量;③可以直接通过接口引用被返回的对象
实现一个Book类,对于正在打折的书可以调用getSaledBook方法获取SaledBook实例,①能够实现SaledBook类的隐藏,②不需要知道SaledBook类的存在,通过Book即可调用
class Book {
protected String name;
protected int price;
private Book() { }
public void setName(String name) {
this.name = name;
}
public void setPrice(int price) {
this.price = price;
}
public String getName() {
return name;
}
public int getPrice() {
return price;
}
public static Book getBook() {
return new Book();
}
public static Book getSaleBook() {
return new SaleBook();
}
private static class SaleBook extends Book{
public void setPrice(int price) {
this.price = (int)(price*0.8);
}
}
}
自行调试:
public class Test {
public static void main(String[] args) {
Book book = Book.getBook();
book.setPrice(12);
Book saleBook = Book.getSaleBook();
saleBook.setPrice(12);
System.out.println(book.getPrice());
System.out.println(saleBook.getPrice());
}
}
4. 所返回的对象的类可以随着每次调用而发生变化,这取决于静态工厂方法的参数值,客户端不知道也不需要知道它们从工厂方法中得到的对象的类
沿用第3条的例子,丢弃getSaledBook方法,统一通过getBook方法获取Book对象,依照传入的参数决定返回的对象
class Book {
protected String name;
protected int price;
private Book() { }
public void setName(String name) {
this.name = name;
}
public void setPrice(int price) {
this.price = price;
}
public String getName() {
return name;
}
public int getPrice() {
return price;
}
public static Book getBook(String type) {
switch (type) {
case "Normal":
return new Book();
case "Sale":
return new SaleBook();
default:
return null;
}
}
private static class SaleBook extends Book{
public void setPrice(int price) {
this.price = (int)(price*0.8);
}
}
}
自行调试:
public class Test {
public static void main(String[] args) {
Book book = Book.getBook("Normal");
book.setPrice(12);
Book saleBook = Book.getBook("Sale");
saleBook.setPrice(12);
System.out.println(book.getPrice());
System.out.println(saleBook.getPrice());
}
}
5. 方法返回对象所属的类,在编写包含该静态工厂方法的类时可以不存在
沿用第4条的例子,如果我们需要再扩展一个Book的子类RiseBook表示价格上涨的书籍,只需要编写该子类然后在静态工厂方法中声明即可
class Book {
protected String name;
protected int price;
private Book() { }
public void setName(String name) {
this.name = name;
}
public void setPrice(int price) {
this.price = price;
}
public String getName() {
return name;
}
public int getPrice() {
return price;
}
public static Book getBook(String type) {
switch (type) {
case "Normal":
return new Book();
case "Sale":
return new SaleBook();
case "Rise":
return new RiseBook();
default:
return null;
}
}
private static class SaleBook extends Book{
public void setPrice(int price) {
this.price = (int)(price*0.8);
}
}
private static class RiseBook extends Book{
public void setPrice(int price) {
this.price = (int)(price*1.2);
}
}
}
自行调试:
public class Test {
public static void main(String[] args) {
Book book = Book.getBook("Normal");
book.setPrice(12);
Book saleBook = Book.getBook("Sale");
saleBook.setPrice(12);
Book riseBook = Book.getBook("Rise");
riseBook.setPrice(12);
System.out.println(book.getPrice());
System.out.println(saleBook.getPrice());
System.out.println(riseBook.getPrice());
}
}
静态工厂方法的劣势:
1. 类如果没有public或者protected的构造器,就不能被继承
考虑到减少API、隐藏实现类的需求,上述例子中SaleBook和RiseBook均为私有类,因此它们无法被继承
2. 程序员很难发现它们
静态工厂方法在文档中没有明确标识,同时需要建立标准的命名习惯,不再赘述
总而言之,静态工厂方法和公有构造器各有用处,但是静态工厂方法通常来说更加合适,第一反应不应该是考虑公有构造器。