很多開發者在面試或排查 Bug 時,都會被「靜態塊、構造塊、構造方法、父子類」的初始化順序繞暈。本文通過兩個完整示例,配合輸出結果與口訣總結,一次講透 Java 類加載與初始化的完整鏈路。
public class Parent {
static String staticStr = "父類靜態變量";
private String str = "父類成員變量";
static {
System.out.println(staticStr);
System.out.println("父類靜態初始化塊");
}
{
System.out.println(str);
System.out.println("父類初始化塊");
}
public Parent() {
System.out.println("父類構造");
}
public static void main(String[] args) {
new Parent(); // 第二行若解註釋,靜態部分不再重複執行
}
}
父類靜態變量 父類靜態初始化塊 父類成員變量 父類初始化塊 父類構造 // 若 new Parent() 兩次,後續只會輸出: // 父類成員變量 // 父類初始化塊 // 父類構造
class Parent {
static String staticStr = "父類靜態變量";
private String str = "父類成員變量";
static { System.out.println(staticStr); System.out.println("父類靜態初始化塊"); }
{ System.out.println(str); System.out.println("父類初始化塊"); }
public Parent() { System.out.println("父類構造"); }
}
class SubClass extends Parent {
static String staticStr = "子類靜態變量";
private String str = "子類成員變量";
static { System.out.println(staticStr); System.out.println("子類靜態初始化塊"); }
{ System.out.println(str); System.out.println("子類初始化塊"); }
public SubClass() { System.out.println("子類構造"); }
public static void main(String[] args) {
System.out.println("子類main方法");
new SubClass();
}
}
父類靜態變量 父類靜態初始化塊 子類靜態變量 子類靜態初始化塊 子類main方法 父類成員變量 父類初始化塊 父類構造 子類成員變量 子類初始化塊 子類構造
new SubClass(),先檢查 SubClass 是否已加載,未加載則觸發加載。加載子類前必須先加載父類(extends Parent),故父類靜態部分優先初始化。<clinit> 方法)。main 方法(屬於子類靜態初始化後)。new SubClass() 觸發實例構造。JVM 先隱式調用 super(),完成父類非靜態成員賦值、構造塊、父類構造方法,再執行子類同類操作。static final 若非編譯期常量,初始化順序同普通靜態變量;final 實例變量必須在構造方法結束前賦值。「靜態父子排排站,非靜父構子構站;定義順序不能亂,靜態只跑第一遍。」
掌握初始化順序,不僅能應付面試,更能避開生產環境中因初始化順序導致的 NPE、邏輯錯誤等隱形坑。建議收藏備查,下次遇到複雜繼承鏈時拿出來對照驗證即可。