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

Java 建構子深度解析:從語法規則到設計模式實戰

     編輯:Java 程式設計入門

建構子 ≠ 方法:本質差異全景圖

建構子雖長得像方法,但本質完全不同。如同鴨嘴獸雖是哺乳動物卻產卵,建構子有獨特規則體系:

維度建構子一般方法
目的建立物件並完成初始化執行業務邏輯、運算、副作用
名稱必須與類別名完全相同慣例小寫開頭、動詞開頭 (calculateTotal)
回傳型別無 (連 void 都不能寫)必須宣告 (含 void)
修飾詞public/protected/private/default + 可加註解同上 + static/final/abstract/synchronized/native/strictfp
呼叫方式只能配合 new / this() / super()物件.方法() / 類別.靜態方法() / 介面.default方法()
繼承性不被繼承,子類別隱含無參建構子呼叫 super()非私有方法可被繼承/覆寫
this 語意呼叫本類別其他建構子 (this(...)),必須為第一行參考當前執行物件實例
super 語意呼叫父類別建構子 (super(...)),必須為第一行呼叫父類別被覆寫方法 (super.method())

建構子鏈:this() 與 super() 協作機制

this():同類別建構子委派

public class Platypus {
    private String name;
    private int age;
    private String habitat;

    // 全參建構子:核心初始化邏輯集中於此
    public Platypus(String name, int age, String habitat) {
        this.name = name;
        this.age = age;
        this.habitat = habitat;
    }

    // 委派給全參建構子,提供預設值
    public Platypus(String name, int age) {
        this(name, age, "澳洲東部溪流");  // 必須第一行
    }

    public Platypus(String name) {
        this(name, 0);  // 連鎖委派
    }

    public Platypus() {
        this("未命名鴨嘴獸");  // 終極委派
    }
}

核心規則:this(...) 必須為建構子第一條語句,否則編譯錯誤。一個建構子只能呼叫一個 this(...)。

super():父類別建構子鏈

class Mammal {
    protected String birthType;
    public Mammal(String birthType) { this.birthType = birthType; }
    public void getBirthInfo() { System.out.println("出生方式: " + birthType); }
}

class Platypus extends Mammal {
    private String name;
    // 子類別建構子隱含/顯式呼叫 super(...)
    public Platypus(String name) {
        super("產卵哺乳");  // 必須第一行,傳遞父類別所需參數
        this.name = name;
    }
    
    @Override
    public void getBirthInfo() {
        super.getBirthInfo();  // 這裡 super 呼叫的是父類別「方法」,非建構子
        System.out.println("鴨嘴獸名稱: " + name);
    }
}

關鍵差異:建構子中 super(...) 呼叫父建構子 (初始化父狀態);方法中 super.method() 呼叫父方法實作 (複用父邏輯)。兩者語法相似但語意截然不同。


建構子設計最佳實務

1. 靜態工廠方法替代公共建構子 (Effective Java Item 1)

public class Order {
    private final String id;
    private final List items;
    private final OrderStatus status;

    private Order(String id, List items) {  // 私有建構子
        this.id = id;
        this.items = List.copyOf(items);
        this.status = OrderStatus.CREATED;
    }

    // 語意清晰的靜態工廠
    public static Order createNew(List items) {
        return new Order(UUID.randomUUID().toString(), items);
    }

    public static Order restoreFromSnapshot(String id, List items, OrderStatus status) {
        Order order = new Order(id, items);
        // 反射或特殊邏輯設定 status,或增加 package-private 建構子
        return order;
    }
}

優點:有名稱、可回傳子型別/快取實例、支援不可變模式、API 演進彈性大。

2. Builder 模式應對多參數建構 (Effective Java Item 2)

public class HttpRequest {
    private final String url;
    private final String method;
    private final Map headers;
    private final String body;
    private final int timeoutMs;
    private final boolean followRedirects;

    private HttpRequest(Builder b) {
        this.url = b.url;
        this.method = b.method;
        this.headers = Map.copyOf(b.headers);
        this.body = b.body;
        this.timeoutMs = b.timeoutMs;
        this.followRedirects = b.followRedirects;
    }

    public static class Builder {
        // 必填
        private final String url;
        private String method = "GET";
        private Map headers = new HashMap<>();
        private String body = "";
        private int timeoutMs = 5000;
        private boolean followRedirects = true;

        public Builder(String url) { this.url = url; }
        public Builder method(String m) { this.method = m; return this; }
        public Builder header(String k, String v) { headers.put(k, v); return this; }
        public Builder body(String b) { this.body = b; return this; }
        public Builder timeout(int ms) { this.timeoutMs = ms; return this; }
        public Builder followRedirects(boolean f) { this.followRedirects = f; return this; }
        public HttpRequest build() { return new HttpRequest(this); }
    }
}

// 使用:流暢 API,參數順序不重要,可選參數一目瞭然
HttpRequest req = new HttpRequest.Builder("https://api.example.com")
    .method("POST")
    .header("Content-Type", "application/json")
    .body("{\"key\":\"value\"}")
    .timeout(10000)
    .build();

3. 不可變物件建構子防禦性複製

public final class Period {
    private final LocalDate start;
    private final LocalDate end;

    public Period(LocalDate start, LocalDate end) {
        this.start = Objects.requireNonNull(start, "start 不可為 null");
        this.end = Objects.requireNonNull(end, "end 不可為 null");
        if (start.isAfter(end)) throw new IllegalArgumentException("start 不得晚於 end");
        // LocalDate 本身不可變,無需複製。若為可變型別 (Date/集合) 必須防禦性複製
    }
    // 僅提供 getter,無 setter
}

進階陷阱與避坑指南

陷阱錯誤示範正確做法原因
建構子中啟動執行緒new Thread(this::run).start();提供 start() 方法由呼叫端控制物件未完全初始化即洩漏 this 引用,導致竞態條件
建構子呼叫可覆寫方法public Parent() { init(); } protected void init() {}建構子僅初始化欄位,init() 改為 private/final 或靜態工廠中呼叫子類別覆寫方法存取尚未初始化的子類別欄位 → NPE/邏輯錯誤
循環委派this(a); this(b); 互相呼叫設計單向委派鏈,終點為全參建構子編譯器無法檢測運行期無限遞迴 → StackOverflowError
大量建構子參數new User(a,b,c,d,e,f,g,h);Builder 模式 / 參數物件 (DTO) / 靜態工廠參數順序易誤、可讀性差、擴充困難

序列化與建構子的特殊關係

  • 反序列化不呼叫建構子,透過 Unsafe.allocateInstance 分配記憶體 → 填充欄位。
  • 若需控制反序列化行為,實作 private void readObject(ObjectInputStream s) 或 readResolve()。
  • serialVersionUID 明確宣告避免版本不相容。

現代語言特性:Record (JDK 14+ Preview / 16+ Standard)

// 自動產生:全參建構子、getter、equals/hashCode、toString、with-er (非標準)
public record OrderId(String value) {
    public OrderId {
        Objects.requireNonNull(value);
        if (value.isBlank()) throw new IllegalArgumentException("訂單號不可空白");
    }
}

// 等價於不可變類別,建構子驗證在 canonical constructor 中
// 使用:new OrderId("ORD-20240101-001")

Record 針對「資料載體」場景徹底簡化建構子樣板碼,配合 compact constructor 實現驗證邏輯集中化。

建構子是物件生命的「出生證明」。好的建構子設計讓非法狀態無法表達 (Make illegal states unrepresentable) —— 這是領域驅動設計與函數式程式設計共同追求的核心原則。掌握建構子鏈、工廠模式、Builder 模式、Record,你就掌握了 Java 物件創建的主導權。

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