1. 程式人生 > >從Java虛擬機器角度分析類的例項化順序

從Java虛擬機器角度分析類的例項化順序

1.首先展示一下例項程式碼(Son.java & Father.java)

public class Father {
    
    public static int a=10;//父類的靜態變數
    static{//父類的靜態程式碼塊
        a=20;
    }
    {//父類的構造程式碼塊
        a=30;
    }
    
    public Father() {//父類的構造方法
        a=40;
    }
}
public class Son extends Father{
    
    public static int
s=10;//子類的靜態變數 public int k=20;//子類的例項變數 static{//子類的靜態程式碼塊 s=20; } {//子類的構造程式碼塊 s=30; } public Son() {//子類的建構函式 s=40; } {//子類的構造程式碼塊 s=50; } }

2.將son.java檔案編譯為son.class檔案,然後使用javap反編譯檢視Son的位元組碼指令來分析Son的載入順序,更利於理解(javap -v -c Son > p.txt)。

3.執行程式碼"new Son();"後,分析類的載入順序。

下面的static{};為<clinit>函式,son();為<init>函式。

  static {};
    descriptor: ()V
    flags: ACC_STATIC
    Code:
      stack=1, locals=0, args_size=0
         0: bipush        10
         2: putstatic     #11                 // Field s:I--------------------------順序執行靜態變數的賦值
         5: bipush        20
         7: putstatic     #11                 //
Field s:I--------------------------順序執行靜態程式碼塊 10: return
  public packet1020.Son();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=2, locals=1, args_size=1
         0: aload_0
         1: invokespecial #16                 // Method packet1020/Father."<init>":()V--------------------執行父類的<init>函式(順序不變,第一個)
         4: aload_0
         5: bipush        20
         7: putfield      #18                 // Field k:I------------------------------------------------按順序收集例項變數賦值
        10: bipush        30
        12: putstatic     #11                 // Field s:I------------------------------------------------按順序收集構造程式碼塊
        15: bipush        50
        17: putstatic     #11                 // Field s:I------------------------------------------------按順序收集構造程式碼塊
        20: bipush        40
        22: putstatic     #11                 // Field s:I------------------------------------------------最後執行自己的建構函式程式碼(順序不變,最後一個)
        25: return

 開始分析:

1.觸發類的載入,在初始化階段,先執行父類<clinit>函式,然後執行子類<clinit>函式,按照順序執行靜態變數賦值與靜態程式碼塊。

2.程式碼中執行了建構函式,所以執行<init>函式。

結論:

1.父類中順序執行靜態變數賦值,靜態程式碼塊

2.子類中順序執行靜態變數賦值,靜態程式碼塊

3.父類中順序執行例項變數賦值,構造程式碼塊

4.父類建構函式

5.子類中順序執行例項變數賦值,構造程式碼塊

6.子類建構函式

 

名字解釋:摘抄自周志明老師的《深入理解Java虛擬機器:JVM高階特性與最佳實踐》

1.有且只有4中情況下必須對類進行初始化(執行<clinit>函式)中的第三種:當初始化一個類時,先初始化父類。這就是為什麼父類的<clinit>函式先於子類的<clinit>函式執行。

2.<clinit>函式:編譯器按照原始碼中的順序自動收集類中的所有靜態變數的賦值動作和靜態程式碼塊中的語句合併而成的。

3.<init>函式:最開始先呼叫父類的<init>函式,然後編譯器按照原始碼中的順序自動收集類中的例項變數的賦值操作和構造程式碼塊中的語句合併,然後插入到建構函式方法前面,最後是程式設計師自己寫的建構函式程式碼。

構造程式碼塊執行順序先於建構函式

<init>(){
  1.呼叫父類<init>方法
  2.順序執行例項變數的賦值操作和構造程式碼塊
  3.程式設計師自己的建構函式方法程式碼
}