java類初始化的順序

Inequality-Sign發表於2018-03-27

先從最簡單的說起,我們一般是這樣理解的:
先初始化靜態 成員/塊 然後是構造程式碼塊,最後是建構函式 看一個例子

這是兩個類ShunXu 和 Other

public class ShunXu  {


    public ShunXu() {
        System.out.println("建構函式");

    }

    public static Other other = new Other();


    static {
        System.out.println("靜態塊");
    }
    {
        System.out.println("構造程式碼塊");
    }


    public static void main(String[] args) {
        ShunXu shunXu = new ShunXu();
    }
}
public class Other {

    {
        System.out.println("我是另一個類的構造程式碼快");
    }
    static {
        System.out.println("我是另一個類的靜態塊");
    }

}

首先明確一點,靜態程式碼塊和靜態成員變數是沒有先後之分,完全按照程式碼的順序執行

執行主函式,首先會裝載ShunXu->構造靜態成員變數other(other中先執行靜態塊,後執行構造程式碼塊)->執行靜態塊->構造程式碼塊->建構函式
因此,輸出結果就是


我是另一個類的靜態塊
我是另一個類的構造程式碼快
靜態塊
構造程式碼塊
建構函式

接下來看一個有些奇怪的例子

現在先不管Other,只關注ShunXu

public class ShunXu  {


    public ShunXu() {
        System.out.println("建構函式");

    }

    static ShunXu shunXu = new ShunXu();
    static ShunXu shunXu2 = new ShunXu();


    static {
        System.out.println("靜態塊");
    }
    {
        System.out.println("構造程式碼塊");
    }


    public static void main(String[] args) {

    }
}

我們在ShunXu內部建立了兩個靜態ShunXu物件

主函式本身什麼也不做,先看看輸出結果

構造程式碼塊
建構函式
構造程式碼塊
建構函式
靜態塊

是不是很神奇,靜態塊竟然最後執行

其實原因不難分析,可以看出建立兩個物件分別呼叫了兩次構造程式碼塊和建構函式,說明構造靜態物件的時候並沒有執行靜態塊,也就是說,靜態塊是由最外層的類來執行的。既然靜態塊寫在了靜態成員變數和後面,那麼它當然就得等靜態成員生成完再執行了

如果將靜態塊放在最前面

public class ShunXu  {


    public ShunXu() {
        System.out.println("建構函式");

    }



    static {
        System.out.println("靜態塊");
    }
    {
        System.out.println("構造程式碼塊");
    }

    static ShunXu shunXu1 = new ShunXu();
    static ShunXu shunXu2 = new ShunXu();


    public static void main(String[] args) {

    }
}

那麼輸出就是

靜態塊
構造程式碼塊
建構函式
構造程式碼塊
建構函式

這驗證我之前的說法:
靜態程式碼塊和靜態成員變數是沒有先後之分,完全按照程式碼的順序執行

那如果我們在主函式中再建立一個ShunXu物件呢

public class ShunXu  {


    public ShunXu() {
        System.out.println("建構函式");

    }

    static ShunXu shunXu1 = new ShunXu();
    static ShunXu shunXu2 = new ShunXu();

    static {
        System.out.println("靜態塊");
    }
    {
        System.out.println("構造程式碼塊");
    }


    public static void main(String[] args) {
        ShunXu xu = new ShunXu();
    }
}

輸出為:

構造程式碼塊
建構函式
構造程式碼塊
建構函式
靜態塊
構造程式碼塊
建構函式

可以看出,這其實並沒有違背先靜態後非靜態的說法

剩下的就是關於父類的說明了

class Parent{
    public Parent(){
        System.out.println("父類構造方法");
    }
    static {
        System.out.println("父類靜態塊");
    }
}


public class ShunXu  extends Parent{


    public ShunXu() {
        System.out.println("建構函式");

    }

    static ShunXu shunXu1 = new ShunXu();
    static ShunXu shunXu2 = new ShunXu();

    static {
        System.out.println("靜態塊");
    }
    {
        System.out.println("構造程式碼塊");
    }


    public static void main(String[] args) {
        ShunXu xu = new ShunXu();
    }
}

輸出結果為


1父類靜態塊

2父類構造方法
3構造程式碼塊
4建構函式

5父類構造方法
6構造程式碼塊
7建構函式

8靜態塊
9父類構造方法
10構造程式碼塊
11建構函式

輸出有些多,我們把它們給分割 1 - 7是兩個靜態成員的構造過程,先對載入其父類 然後呼叫父類構造方法 然後自身構造 8 - 11就是一個正常的物件的構造過程

說複雜的確有點,想通了其實問題就不大

相關文章