Java 建構子 (Constructor)

建構子是在建立物件時自動執行的特殊方法,用來初始化物件。

基本語法

建構子的特點:

  • 名稱與類別相同
  • 沒有回傳型別(連 void 都沒有)
public class Person {
    private String name;
    private int age;
    
    // 建構子
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
}

// 使用
Person p = new Person("Alice", 25);

預設建構子

如果沒有定義任何建構子,Java 會自動提供一個無參數的預設建構子:

public class Dog {
    String name;
    // Java 自動提供預設建構子
}

Dog d = new Dog();  // 使用預設建構子

但是,一旦定義了建構子,就不會有預設建構子:

public class Dog {
    String name;
    
    public Dog(String name) {
        this.name = name;
    }
}

// Dog d = new Dog();  // 錯誤!沒有無參數建構子
Dog d = new Dog("Buddy");  // OK

建構子多載

可以定義多個建構子:

public class Person {
    private String name;
    private int age;
    
    // 無參數建構子
    public Person() {
        this.name = "Unknown";
        this.age = 0;
    }
    
    // 只有名稱
    public Person(String name) {
        this.name = name;
        this.age = 0;
    }
    
    // 完整參數
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
}

// 使用不同的建構子
Person p1 = new Person();              // Unknown, 0
Person p2 = new Person("Alice");       // Alice, 0
Person p3 = new Person("Bob", 25);     // Bob, 25

this() 呼叫其他建構子

使用 this() 可以呼叫同一類別的其他建構子:

public class Person {
    private String name;
    private int age;
    
    public Person() {
        this("Unknown", 0);  // 呼叫完整參數的建構子
    }
    
    public Person(String name) {
        this(name, 0);  // 呼叫完整參數的建構子
    }
    
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
}
this() 必須是建構子的第一個敘述。

初始化順序

public class Example {
    private int a = 1;  // 1. 實例變數初始化
    
    {
        // 2. 實例初始化區塊
        System.out.println("初始化區塊");
    }
    
    public Example() {
        // 3. 建構子
        System.out.println("建構子");
    }
}

私有建構子

私有建構子可以防止外部建立物件,常用於單例模式:

public class Singleton {
    private static Singleton instance;
    
    // 私有建構子
    private Singleton() { }
    
    public static Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}

// Singleton s = new Singleton();  // 錯誤!
Singleton s = Singleton.getInstance();  // OK

複製建構子

建立新物件時複製另一個物件的值:

public class Person {
    private String name;
    private int age;
    
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
    
    // 複製建構子
    public Person(Person other) {
        this.name = other.name;
        this.age = other.age;
    }
}

Person original = new Person("Alice", 25);
Person copy = new Person(original);  // 複製

建構子與繼承

子類別建構子會先呼叫父類別建構子:

public class Animal {
    String name;
    
    public Animal(String name) {
        this.name = name;
        System.out.println("Animal 建構子");
    }
}

public class Dog extends Animal {
    String breed;
    
    public Dog(String name, String breed) {
        super(name);  // 呼叫父類別建構子
        this.breed = breed;
        System.out.println("Dog 建構子");
    }
}

Dog d = new Dog("Buddy", "Golden");
// 輸出:
// Animal 建構子
// Dog 建構子

最佳實踐

  1. 初始化所有欄位:確保物件建立後處於有效狀態
  2. 驗證參數:在建構子中驗證輸入
  3. 避免太多參數:考慮使用 Builder 模式
public class User {
    private final String name;
    private final String email;
    
    public User(String name, String email) {
        // 驗證參數
        if (name == null || name.isEmpty()) {
            throw new IllegalArgumentException("名稱不能為空");
        }
        if (email == null || !email.contains("@")) {
            throw new IllegalArgumentException("無效的 email");
        }
        
        this.name = name;
        this.email = email;
    }
}