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

Java 類初始化順序深度解析:單類與繼承體系全圖解

     編輯:Java 綜合教學

Java 類初始化順序深度解析:單類與繼承體系全圖解

很多開發者在面試或排查 Bug 時,都會被「靜態塊、構造塊、構造方法、父子類」的初始化順序繞暈。本文通過兩個完整示例,配合輸出結果與口訣總結,一次講透 Java 類加載與初始化的完整鏈路。

核心結論先行(背得出來就贏了)

  1. 靜態優於非靜態:靜態成員/塊 → 非靜態成員/塊 → 構造方法
  2. 父類優於子類:父類靜態 → 子類靜態 → 父類非靜態 → 父類構造 → 子類非靜態 → 子類構造
  3. 同級按定義順序:同層級(如多個靜態塊)按在類中出現的先後順序執行
  4. 靜態只執行一次:無論 new 多少對象,靜態部分僅在類首次加載時執行一次

場景一:單類無繼承

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方法
父類成員變量
父類初始化塊
父類構造
子類成員變量
子類初始化塊
子類構造

為什麼是這個順序?JVM 底層視角

  1. 類加載階段:JVM 發現 new SubClass(),先檢查 SubClass 是否已加載,未加載則觸發加載。加載子類前必須先加載父類(extends Parent),故父類靜態部分優先初始化。
  2. 鏈接/準備階段:分配靜態變量內存並賦默認值,再執行靜態賦值語句與靜態塊(<clinit> 方法)。
  3. 初始化階段:執行 main 方法(屬於子類靜態初始化後)。
  4. 實例化階段:new SubClass() 觸發實例構造。JVM 先隱式調用 super(),完成父類非靜態成員賦值、構造塊、父類構造方法,再執行子類同類操作。

常見坑點提醒

  • 靜態方法不可被覆寫:子類靜態方法若與父類同名,屬於「隱藏」而非覆寫,調用時看引用類型而非對象類型。
  • 構造方法中調用可覆寫方法危險:父類構造執行時子類非靜態成員尚未初始化,若調用被子類覆寫的方法,可能讀到默認值(null/0)。
  • final 變量延遲初始化:static final 若非編譯期常量,初始化順序同普通靜態變量;final 實例變量必須在構造方法結束前賦值。

一句話速記口訣

「靜態父子排排站,非靜父構子構站;定義順序不能亂,靜態只跑第一遍。」

掌握初始化順序,不僅能應付面試,更能避開生產環境中因初始化順序導致的 NPE、邏輯錯誤等隱形坑。建議收藏備查,下次遇到複雜繼承鏈時拿出來對照驗證即可。

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