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

Java 類與對象初始化順序深度解析:靜態/實例字段、代碼塊、構造器執行流程

     編輯:Java 程式設計入門

Java 類加載與對象創建的初始化順序是面試高頻考點,也是理解框架底層 (Spring Bean 初始化、單例模式) 的基石。本文通過編譯器視角還原字節碼層面的執行流程,提供標準化順序模型與實戰驗證代碼。

類初始化 (靜態上下文)

觸發時機 (JLS §12.4.1)

  • 主動引用:new、靜態字段讀寫、靜態方法調用、反射 Class.forName、子類初始化觸發父類
  • 被動引用:常量字段 (final static 編譯期常量)、數組創建、父類已初始化

執行順序鐵律

  1. 父類靜態字段聲明順序賦值
  2. 父類靜態代碼塊 (static {}) 順序執行
  3. 子類靜態字段聲明順序賦值
  4. 子類靜態代碼塊 順序執行

核心原則:按源碼**從上到下、從父到子**順序,字段聲明賦值與靜態塊**交織執行**,而非分階段。

編譯器視角:源碼如何轉換


// 源碼
public class Person {
    public static String name = "張三";
    public static int age;
    static { age = 20; System.out.println("靜態塊1"); }
    public static String address;
    static { address = "北京"; age = 34; }
}

// 編譯器生成的  邏輯等效代碼
public class Person {
    public static String name;
    public static int age;
    public static String address;
    static {
        name = "張三";      // 字段聲明賦值
        age = 20;           // 靜態塊1
        System.out.println("靜態塊1");
        address = "北京";   // 靜態塊2
        age = 34;
    }
}

對象初始化 (實例上下文)

觸發時機

  • new 關鍵字、反射 Constructor.newInstance()、反序列化、clone()

執行順序鐵律 (單一類)

  1. 父類實例字段聲明順序賦值
  2. 父類實例代碼塊 ({}) 順序執行
  3. 父類構造器體執行
  4. 子類實例字段聲明順序賦值
  5. 子類實例代碼塊 順序執行
  6. 子類構造器體執行

編譯器視角:構造器 生成邏輯


// 源碼
public class Person {
    { name = "李四"; age = 56; System.out.println("實例塊"); address = "上海"; }
    public String name = "張三";
    public int age = 29;
    public String address = "北京";
    public Person() { name = "趙六"; age = 23; address = "上海市"; }
}

// 編譯器生成  等效代碼
public Person() {
    // 1. 隱式 super() 已完成父類初始化
    name = "李四";          // 實例塊 (按源碼順序)
    age = 56;
    System.out.println("實例塊");
    address = "上海";
    name = "張三";          // 字段聲明賦值 (按源碼順序)
    age = 29;
    address = "北京";
    name = "趙六";          // 構造器體
    age = 23;
    address = "上海市";
}

關鍵觀察

  • 實例代碼塊 **早於** 字段聲明賦值執行 (源碼順序決定)
  • 字段聲明賦值 **早於** 構造器體執行
  • 構造器第一行隱式 super() 觸發父類完整初始化

綜合驗證代碼


class Parent {
    static { System.out.println("1. Parent static block"); }
    static String pStatic = init("2. Parent static field");
    { System.out.println("3. Parent instance block"); }
    String pInst = init("4. Parent instance field");
    Parent() { System.out.println("5. Parent constructor"); }
    static String init(String s) { System.out.println(s); return s; }
}

class Child extends Parent {
    static { System.out.println("6. Child static block"); }
    static String cStatic = init("7. Child static field");
    { System.out.println("8. Child instance block"); }
    String cInst = init("9. Child instance field");
    Child() { System.out.println("10. Child constructor"); }
    static String init(String s) { System.out.println(s); return s; }
}

public class InitOrderDemo {
    public static void main(String[] args) {
        System.out.println("=== new Child() ===");
        new Child();
    }
}

/* 輸出順序:
1. Parent static block
2. Parent static field
6. Child static block
7. Child static field
3. Parent instance block
4. Parent instance field
5. Parent constructor
8. Child instance block
9. Child instance field
10. Child constructor
*/

特殊場景與陷阱

1. final static 基本類型/字符串 (編譯期常量)


static final int CONST = 100;        // 編譯期常量,不觸發類初始化
static final String STR = "hello";   // 同上
static final Object OBJ = new Object(); // 非常量,觸發初始化

2. 循環依賴初始化


class A { static int x = B.y + 1; }
class B { static int y = A.x + 1; }
// 首次訪問 A.x → 初始化 A → 需 B.y → 初始化 B → 需 A.x (正在初始化,默認值 0) → y=1 → x=2

3. 構造器中調用可覆寫方法


class Base {
    Base() { init(); } // 危險!
    void init() { System.out.println("Base"); }
}
class Derived extends Base {
    String field = "init";
    @Override void init() { System.out.println(field); } // field 尚未初始化 → null
}
new Derived(); // 輸出 null,而非 "init"

4. 線程安全與類初始化

  • JVM 保證 線程安全 (內部鎖)
  • 單例模式利用此特性:static final Instance INSTANCE = new Instance(); (飽漢式線程安全)

總結速查表

階段順序關鍵字
類加載1. 父類靜態字段/塊 (源碼順)static
2. 子類靜態字段/塊 (源碼順)static
對象創建3. 父類實例字段/塊 (源碼順){}
4. 父類構造器體constructor
5. 子類實例字段/塊 (源碼順){}
6. 子類構造器體constructor

口訣:靜態先於實例、父類先於子類、字段塊按源碼序、構造器最後跑。

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