到底有哪些建構式


在建構物件時,如果想要指定物件建立時的初始方式,可以定義類別的建構式。建構式像是與類別名稱同名,無需傳回值的方法。
public class Some {
    private int a;
    private String text;
    public Some(int a, String text) {
        this.a = a;
        this.text = text;
    }
}

當你這麼建構它時,事實上,成員a與text初始了兩次
Some s = new Some(1, "Java");

建構物件時,成員就會初始化,如果沒有指定初始值,則會使用預設值初始化。整數會初始為0,浮點數會初始為0.0,參考會初始為null。之後執行建構式進行當中所指定的初始內容。所以上例中,建構物件時,a與text分別先初始為0與null,再於建構式中,被設定為建構式參數所指定的值。

如果你定義類別時,沒有指定任何的建構式,那麼編譯器會自動加入一個無引數、內容為空的建構式。例如:
public class Some {
}

這個類別在反組譯之後,會變成這樣:
public class Some {
    public Some() {
    }
}

所以,你才可以這麼撰寫:
Some s = new Some();

有人稱編譯器自動為你加入的建構式為預設建構式(default constructor),你自己寫的無引數建構式不稱為預設建構式,其實這只是名詞問題,別太特意去區分。

如果你定義了自己的建構式,那麼編譯器就不會鷄婆地為你建立建構式。如果你這麼寫:
public class Some {
    public Some(int a) {
    }
}

那就不可以直接:
Some s = new Some();

一般建議,如果自己定義了有引數的建構式,也可以加入無引數建構式,即使內容為空也無所謂,這是為了日後使用上的彈性起見。例如在運用Reflection機制生成物件時,或者是繼承時呼叫父類別建構式時。

無引數建構式的使用,通常是為了一些預設情況,例如這樣寫其實是多餘的:
public class Some {
    private int a;
    private String text;
    public Some() {
a = 0;
        text = null;
    }
}

一個使用的時機也許像是:
public class Some {
    private int a;
    private String text;
public Some () {
this(10, "Java");
}
    public Some(int a, String text) {
        this.a = a;
        this.text = text;
    }
}

這邊示範了this關鍵字的引數使用,這表示呼叫另一個有引數的建構式,並指定建構時的值。所以這麼寫時:
Some s = new Some();

物件的a就會設定為10,text參考至"Java"。如此可以避免重複程式碼的撰寫,就不用像這樣寫:
public class Some {
    private int a = 10;
    private String text = "Java";
    public Some () {
        this(10, "Java");
    }
    public Some(int a, String text) {
        this.a = a;
        this.text = text;
    }
}

你也許看過這樣的寫法:
public class Some {
    private int a;
    private String text;
    {
        a = 10;
        text = "Java";
    }
}

那個初始a與text的寫法,並不是建構式,而是非靜態類別成員初始化區塊,讓你明確地在區塊中初始某些類別成員。

如果類別有繼承關係,在建構子類別實例後,會先進行父類別定義的初始流程,再進行子類別中定義的初始流程,也就是建構子類別實例後,會先執行父類別建構式定義的流程,再執行子類別建構式定義的流程。如果沒有指定執行哪個父類別建構式,則預設就是呼叫父類別無引數建構式。

所以你這麼寫時:
public class Some {
}

public class Other extends Some {
}

相當於這麼寫:
public class Some {
    public Some() {
    }
}

public class Other extends Some {
    public Other() {
        super();
    }
}

別忘了,沒有定義建構式時,編譯器會自動加上無引數建構式。這邊你看到super關鍵字的使用,你可用以呼叫父類別建構式。例如:
public class Some {
    public Some() {
    }
    public Some(int a) {
    }
}

public class Other extends Some {
    public Other() {
        super(10);
    }
}

如果你這麼寫,無法通過編譯:
public class Some {
    public Some(int a) {
    }
}

public class Other extends Some {
    public Other() {
    }
}

在 定義建構式時,如果沒有明確指定super(),那麼就是呼叫父類別無引數建構式。上例中,父類別定義了有引數的建構式,所以編譯器不會再自動加上無引數 建構式,而在繼承的Other中,因為沒有使用super()指定使用哪個父類別建構式,所以預設就是呼叫父類別無引數建構式,但父類別中並不存在無引數 建構式,因此發生編譯錯誤。

建構子類別的物件時,父類別的物件會先行建構,所以父類別成員變數初始化的動作也先進行,所以順序是父類別成員變數、父類別建構式、子類別成員變數、子類別建構式。