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

Java 對象實例化時機深度解析:構造器之前已創建?

     編輯:Java 相關知識

常見誤區

許多開發者認為:「調用構造函數後,對象才被實例化」。因為構造函數執行完才能調用實例方法,否則會 NullPointerException。但這是錯誤的。

證據一:構造器內可用 this

public class Test {
    public Test() {
        this.doSomething(); // 構造器內已可操作 this
    }
    private void doSomething() {
        System.out.println("do init");
    }
}

說明:進入構造器前,對象實例已創建完畢,構造器僅負責初始化。

繼承鏈下的實例化順序

public class BaseClass {
    public BaseClass() {
        System.out.println("create base");
    }
}
public class SubClass extends BaseClass {
    public SubClass() {
        System.out.println("create sub");
    }
    public static void main(String[] args) {
        new SubClass(); // 輸出:create base → create sub
    }
}

直觀以為:實例化父類 → 初始化父類 → 實例化子類 → 初始化子類。錯誤!

反直覺的證據:父類構造器調用子類覆寫方法

public class BaseClass {
    public BaseClass() {
        System.out.println("create base");
        init(); // 調用 init()
    }
    protected void init() { System.out.println("in base init"); }
}

public class SubClass extends BaseClass {
    int i = 1024;
    String s = "13 leaf";
    
    public SubClass() {
        System.out.println("create sub");
        init();
    }
    @Override
    protected void init() {
        System.out.println("now class: " + this.getClass().getSimpleName());
        System.out.println("in SubClass");
        System.out.println(i); // 這裡 i 是多少?
        System.out.println(s);
    }
    public static void main(String[] args) {
        new SubClass();
    }
}

執行順序與輸出

create base
now class: SubClass
in SubClass
0          // i 仍為默認值 0!
null       // s 仍為默認值 null!
create sub
now class: SubClass
in SubClass
1024       // i 已初始化
13 leaf    // s 已初始化

字節碼揭秘:實例化與初始化分離

public SubClass();
  aload_0              // this
  invokespecial BaseClass()  // 1. 調用父類構造器
  aload_0
  sipush 1024          // 2. 初始化子類字段 i
  putfield SubClass.i
  aload_0
  ldc "13 leaf"        // 3. 初始化子類字段 s
  putfield SubClass.s
  getstatic System.out
  ldc "create sub"
  invokevirtual println
  aload_0
  invokevirtual init() // 4. 調用子類 init()
  return

核心結論

  1. 對象實例在構造器執行前已創建完成(內存分配、默認值初始化)
  2. 父類構造器執行時,子類字段尚未初始化(仍為默認值 0/false/null)
  3. 子類字段初始化發生在父類構造器返回後、子類構造器體執行前
  4. 危險場景:父類構造器調用可覆寫方法 → 子類方法讀取未初始化字段 → 隱蔽 Bug

最佳實踐

  • 構造器中避免調用可覆寫方法(private/final/static 方法安全)
  • 若必須調用,文檔明確警告子類:字段可能未初始化
  • 理解「實例化 ≠ 初始化」,調試時關注字節碼順序
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved