首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 綜合教學

Effective Java 精讀:靜態工廠方法替代構造器的 4 大優勢與 2 大缺點

     編輯:Java 綜合教學

Effective Java 精讀:靜態工廠方法替代構造器的 4 大優勢與 2 大缺點

本文基於《Effective Java》第 1 條「考慮用靜態工廠方法代替構造器」展開,結合 JDK 源碼與實戰案例深度解析。

什麼是靜態工廠方法?

類提供一個 public static 方法返回該類(或子類)實例,而非 public 構造器。經典例子:Boolean.valueOf(String)。

// 構造器方式 - 每次新建對象
Boolean b1 = new Boolean("true");

// 靜態工廠方式 - 復用常量池對象
Boolean b2 = Boolean.valueOf("true"); // 返回 Boolean.TRUE / Boolean.FALSE

辨析:靜態工廠方法 ≠ 工廠方法模式。前者是類內部靜態方法;後者是設計模式,涉及多個工廠/產品類。靜態工廠更接近「簡單工廠」,但通常只產出本類或子類實例。

四大核心優勢

1. 名字有語義,可讀性強

構造器名字固定為類名,參數列表相同無法重載。靜態工廠可起語義化名字:

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);

2. 可控制實例創建,避免不必要對象

  • 單例/常量池:Boolean.valueOf、Integer.valueOf(-128~127) 復用緩存對象
  • 延遲初始化:首次調用時才創建(雙檢鎖/靜態內部類)
  • 對象池模式:資源昂貴對象(數據庫連接、線程)循環復用

3. 可返回任意子類型,API 更靈活

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,具體實現類可隨時替換/新增,構造器做不到這點。

4. 泛型類構造更簡潔(類型推導)

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

實戰決策清單

  1. 需要語義化命名區分多種創建方式?→ 用靜態工廠
  2. 頻繁創建相同值對象(Boolean、小整數、枚舉)?→ 用靜態工廠緩存
  3. API 需返回接口/抽象類,隱藏具體實現?→ 用靜態工廠
  4. 類需被廣泛繼承(如框架基類)?→ 保留 protected 構造器,慎用純靜態工廠
  5. 簡單場景、無上述需求?→ 直接用構造器,簡單即是美

Joshua Bloch 原話:「靜態工廠方法與構造器各有優劣,但靜態工廠方法通常更優。不要盲目為類提供 public 構造器,先評估是否適合靜態工廠。」

下次寫 new Xxx() 時,停下來想 3 秒:這裡換成 Xxx.of() 會不會更好?

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved