本文基於《Effective Java》第 1 條「考慮用靜態工廠方法代替構造器」展開,結合 JDK 源碼與實戰案例深度解析。
類提供一個 public static 方法返回該類(或子類)實例,而非 public 構造器。經典例子:Boolean.valueOf(String)。
// 構造器方式 - 每次新建對象
Boolean b1 = new Boolean("true");
// 靜態工廠方式 - 復用常量池對象
Boolean b2 = Boolean.valueOf("true"); // 返回 Boolean.TRUE / Boolean.FALSE
辨析:靜態工廠方法 ≠ 工廠方法模式。前者是類內部靜態方法;後者是設計模式,涉及多個工廠/產品類。靜態工廠更接近「簡單工廠」,但通常只產出本類或子類實例。
構造器名字固定為類名,參數列表相同無法重載。靜態工廠可起語義化名字:
class RandomIntGenerator {
private final int min, max;
private RandomIntGenerator(int min, int max) { this.min = min; this.max = max; }
// 語義一目瞭然,且可繞過重載限制
public static RandomIntGenerator between(int min, int max) {
return new RandomIntGenerator(min, max);
}
public static RandomIntGenerator biggerThan(int min) {
return new RandomIntGenerator(min, Integer.MAX_VALUE);
}
public static RandomIntGenerator smallerThan(int max) {
return new RandomIntGenerator(Integer.MIN_VALUE, max);
}
}
// 調用端極其清晰
RandomIntGenerator r1 = RandomIntGenerator.between(1, 10);
RandomIntGenerator r2 = RandomIntGenerator.biggerThan(100);
RandomIntGenerator r3 = RandomIntGenerator.smallerThan(-50);
Boolean.valueOf、Integer.valueOf(-128~127) 復用緩存對象public interface Dog {}
class RedDog implements Dog {}
class BlackDog implements Dog {}
public class DogFactory {
public static Dog getInstance(String color) {
return "red".equalsIgnoreCase(color) ? new RedDog() : new BlackDog();
}
}
調用方依賴接口 Dog,具體實現類可隨時替換/新增,構造器做不到這點。
class MyMap<K,V> {
private MyMap() {}
public static <K,V> MyMap<K,V> newInstance() {
return new MyMap<>(); // 鑽石運算符 Java 7+
}
}
// 調用無需重複泛型參數
MyMap<String, String> map = MyMap.newInstance();
| 缺點 | 影響 | 應對策略 |
|---|---|---|
| 無 public/protected 構造器 → 無法繼承 | 子類無法調用 super() |
若需繼承,保留 protected 構造器;或改用 Builder 模式 |
| 無明確標識,易與普通靜態方法混淆 | API 使用者難發現 | 遵循命名慣例:valueOf/of/getInstance/newInstance/create;加強 Javadoc |
Joshua Bloch 原話:「靜態工廠方法與構造器各有優劣,但靜態工廠方法通常更優。不要盲目為類提供 public 構造器,先評估是否適合靜態工廠。」
下次寫 new Xxx() 時,停下來想 3 秒:這裡換成 Xxx.of() 會不會更好?