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


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

我們知道一個類中具有類變量、類方法和構造器(方法中的局部變量不討論,他們是在方法調用時才被初始化),當我們初始化創建一個類對象時,其初始化的順序為:先初始化類變量,再執行構造器方法。

代碼驗證:

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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM