淺談Java類中的變數初始化順序

樂派cyh發表於2021-08-04

一、變數與構造器的初始化順序

我們知道一個類中具有類變數、類方法和構造器(方法中的區域性變數不討論,他們是在方法呼叫時才被初始化),當我們初始化建立一個類物件時,其初始化的順序為:先初始化類變數,再執行構造器方法。

程式碼驗證:

public class Demo01 {
    public int a1 = 1;
    public String a2 = "initiaied!";

    public Demo01() {
        System.out.println(a1);
        System.out.println(a2);
        System.out.println("構造器方法被執行");
    }

    public static void main(String[] args) {
        Demo01 demo01 = new Demo01();
    }
}

執行結果:
     1
     initiaied!
     構造器方法被執行

可以看出,當我們建立一個Demo01物件時,先初始化了變數a1和a2,然後執行構造器方法。

二、靜態變數與非靜態變數的初始化順序

靜態變數是屬於類本身,無論建立多少個物件,靜態變數都只有一份儲存區域,因此他會在類首次被訪問或者首次建立類物件時被初始化,而且有且只能初始化一次。

而非靜態變數是屬於每個物件,他是在建立每個物件時都初始化一次。因此,靜態變數要先於非靜態進行初始化。例子:

public class Demo02 {
    public Cup cup1 = new Cup(1);
    public static Cup cup2 = new Cup(2);

    public Demo02() {
        System.out.println("Demo02構造器被執行!");
    }

    public static void main(String[] args) {
        Demo02 demo02 = new Demo02();
        Demo02 demo02_01 = new Demo02();
    }
}

class Cup {
    public Cup(int i) {
        System.out.println("Cup->" + i);
    }
}


執行結果:
        Cup->2
        Cup->1
        Demo02構造器被執行!
        Cup->1
        Demo02構造器被執行

當程式要執行main方法時,會先載入Demo02類檔案,在載入時就會先初始化靜態變數cup2,因此控制檯輸出"cup->2"。

類載入完後,開始執行main方法,建立demo02物件,這時就會初始化類中的非靜態變數cup1,控制檯輸出"cup->1",然後執行構造器方法。建立第二個物件時,只初始化cup1,cup2為靜態變數只初始化一次。

三、靜態程式碼塊與非靜態程式碼塊

靜態程式碼塊本質上就是一段靜態變數的程式碼,其初始化和靜態變數沒有區別:當類首次被訪問或者首次建立該類物件時被初始化,並且只初始化一次。

非靜態程式碼塊就是一段非靜態變數的程式碼,他和非靜態變數的初始化沒有區別。

public class Demo03 {
    static Table table1;
    Table table2;

    static {
        table1 = new Table(1);
    }

    {
        table2 = new Table(2);
    }

    public Demo03() {
        System.out.println("Demo03構造器被執行");
    }

    public static void main(String[] args) {
        new Demo03();
    }
}

class Table {
    public Table(int i) {
        System.out.println("Table->" + i);
    }
}

執行結果:
        Table->1
        Table->2
        Demo03構造器被執行

四、父類與子類的初始化順序

沒有父類就沒有子類,因此無論是類載入還是建立例項物件,父類都優先於子類進行初始化。

public class Demo04 extends Insect {
    private int k = fun("Demo04中的k被初始化");
    private static int x2 = fun("Demo04中的x2被初始化");

    public Demo04() {
        System.out.println("k=" + k);
        System.out.println("j=" + j);
    }

    public static void main(String[] args) {
        Demo04 demo04 = new Demo04();
    }
}

class Insect {
    public int i = 9;
    public int j;
    public static int x1 = fun("Insect中的x1被初始化");

    public Insect() {
        System.out.println("i=" + i + ",j=" + j);
        j = 39;
    }

    public static int fun(String s) {
        System.out.println(s);
        return 47;
    }
}
執行結果:
        Insect中的x1被初始化
        Demo04中的x2被初始化
        i=9,j=0
        Demo04中的k被初始化
        k=47
        j=39

當執行main方法時,載入器開始載入Demo04類檔案,在載入過程中,載入器會注意到他有一個父類Insect還沒被載入,因此會先載入父類Insect檔案。

類載入過程中會完成靜態變數的初始化(此時並不執行構造器方法,構造器方法只有在建立物件時呼叫),Insect類載入完成後,接著載入Demo04類,都載入完成後,就開始執行main方法中的程式碼,建立Demo04物件。

由於繼承關係,因此先建立父類Insect的例項物件,因此父類中的變數初始化和構造器先被執行,然後在初始化子類Demo04中的非靜態變數和執行構造器方法。

五、總結

最後放一段程式碼,把前面所說情況都放在一起。

public class Son extends Father {
    int m = fun("Son中的m 被初始化");

    public Son() {
        System.out.println("m = " + m);
        System.out.println("j = " + j);
    }

    public static int x3 = fun("Son中的x3 被初始化");

    public static void main(String[] args) {
        Son son = new Son();
    }
}

class Father extends GrandFather {

    public int k = fun("Father中的k被初始化");
    public static int x2 = fun("Father中的x2被初始化");

    public Father() {
        System.out.println("k=" + k);
        System.out.println("j=" + j);
    }
}

class GrandFather {
    public int i = 9;
    public int j;
    public static int x1 = fun("GrandFather中的x1被初始化");

    public GrandFather() {
        System.out.println("i=" + i + ",j=" + j);
        j = 39;
    }

    public static int fun(String s) {
        System.out.println(s);
        return 47;
    }
}
執行結果:
        GrandFather中的x1被初始化
        Father中的x2被初始化
        Son中的x3 被初始化
        i=9,j=0
        Father中的k被初始化
        k=47
        j=39
        Son中的m 被初始化
        m = 47
        j = 39

 

相關文章