在 Java 物件導向程式設計中,理解類別載入與初始化的順序至關重要,特別是涉及繼承關係時。本文透過完整的程式碼範例與輸出結果,詳細解析父類與子類的靜態/非靜態成員、初始化區塊、建構子的執行順序。
class Parent {
// 靜態變數
public static String p_StaticField = "父類--靜態變量";
// 實例變數
public String p_Field = "父類--變量";
// 靜態初始化區塊
static {
System.out.println(p_StaticField);
System.out.println("父類--靜態初始化塊");
}
// 實例初始化區塊
{
System.out.println(p_Field);
System.out.println("父類--初始化塊");
}
// 建構子
public Parent() {
System.out.println("父類--構造器");
}
}
public class SubClass extends Parent {
// 靜態變數
public static String s_StaticField = "子類--靜態變量";
// 實例變數
public String s_Field = "子類--變量";
// 靜態初始化區塊
static {
System.out.println(s_StaticField);
System.out.println("子類--靜態初始化塊");
}
// 實例初始化區塊
{
System.out.println(s_Field);
System.out.println("子類--初始化塊");
}
// 建構子
public SubClass() {
// super(); // 隱式呼叫
System.out.println("子類--構造器");
}
// 程式進入點
public static void main(String[] args) {
System.out.println("*************in main***************");
new SubClass();
System.out.println("*************second subClass***************");
new SubClass();
}
}
父類--靜態變量
父類--靜態初始化塊
子類--靜態變量
子類--靜態初始化塊
*************in main***************
父類--變量
父類--初始化塊
父類--構造器
子類--變量
子類--初始化塊
子類--構造器
*************second subClass***************
父類--變量
父類--初始化塊
父類--構造器
子類--變量
子類--初始化塊
子類--構造器
當 JVM 首次使用 SubClass 時(此處為 main 方法),會依序載入父類再載入子類:
這就是為何前四行輸出均為靜態相關內容,且父類優於子類。
每次建立 SubClass 實例時,遵循以下順序:
子類建構子預設會隱式呼叫 super(),這強制父類建構子優先執行。若父類無無參建構子,子類必須顯式呼叫 super(參數),否則編譯錯誤。
若 main 方法無建立實例動作,僅輸出靜態初始化部分,證實實例初始化僅在物件建立時觸發。
| 階段 | 順序 | 說明 |
|---|---|---|
| 類別載入 | 1 | 父類靜態變數與靜態區塊 |
| 2 | 子類靜態變數與靜態區塊 | |
| 物件建立 | 3 | 父類實例變數與實例區塊 |
| 4 | 父類建構子 | |
| 5 | 子類實例變數與實例區塊 | |
| 6 | 子類建構子 |
super() 隱式呼叫機制,設計建構子鏈時需謹慎