類載入順序
目錄
一、類載入做了哪些事?
之前沒有進行類載入
1.類載入,同時初始化類中靜態的屬性(賦預設值)
2.執行靜態程式碼塊
3.分配記憶體空間,同時初始化非靜態的屬性(賦預設值)
4.如果宣告屬性的同時有顯示的賦值,那麼進行顯示賦值把預設值覆蓋
5.執行匿名程式碼塊
6.執行構造器
7.返回記憶體地址
二、類載入的順序
1.static 變數
2.static 程式碼塊
3.成員變數
4.匿名塊
5.構造器
ps:先載入父類,再載入子類;
三、一個具體說明的例子
其中D繼承C,C繼承B,E,F是單獨的類,主方法在ExaminationDemo 中。
public class ExaminationDemo { public static void main(String[] args) { System.out.println("1執行 ExaminationDemo 中的 main 函式, 建立 D 類例項"); new D(); } } class E { // E的構造器 E() { System.out.println("8執行 E 的建構函式"); } // E的普通成員方法 public void funcOfE() { System.out.println("12執行 E 的函式"); } } class F { // F的構造器 F() { System.out.println("2執行 F 的建構函式"); } // F的普通成員方法 public void funcOfF() { System.out.println("4執行 F 的函式"); } } class B { //new了一個E物件 E e = new E(); //B的靜態成員變數 static F f = new F(); //B的普通成員變數 public String sb = getSb(); //B的靜態程式碼塊 static { System.out.println("3執行 B 類的 static 塊(B 包含 E 類的成員 變數,包含靜態 F 類成員變數)"); f.funcOfF(); } //B的匿名程式碼塊 { System.out.println("10執行 B 例項的普通初始化塊"); } //B的構造器 B() { System.out.println("11執行 B 類的建構函式(B 包含 E 類的成員變 量,包含靜態 F 類成員變數)"); } //B的普通成員方法 public String getSb() { System.out.println("9初始化 B 的例項成員變數 sb"); return "sb"; } } class C extends B { // C的靜態程式碼塊 static { System.out.println("5執行 C 的 static 塊(C 繼承 B)"); } // C的匿名程式碼塊 { System.out.println("13執行 C 的普通初始化塊"); } // C的構造器 C() { System.out.println("14執行 C 的建構函式(C 繼承 B)"); } } class D extends C { // D的靜態成員變數 public String sd1 = getSd1(); // D的普通成員變數 public static String sd = getSd(); // D的靜態程式碼塊 static { System.out.println("7執行 D 的 static 塊(D 繼承 C)"); } // D的匿名程式碼塊 { System.out.println("16執行 D 例項的普通初始化塊"); } // D的構造器 D() { System.out.println("17執行 D 的建構函式(D 繼承 C);父類 B 的實 例成員變數 sb 的值為:" + sb + ";本類 D 的 static 成員變數 sd 的值為:" + sd + "; 本類 D 的例項成員變數 sd1 的值是:" + sd1); } // D的靜態成員方法(呼叫時才載入) static public String getSd() { System.out.println("6初始化 D 的 static 成員變數 sd"); return "sd"; } // D的普通成員方法 public String getSd1() { System.out.println("15初始化 D 的例項成員變數 sd1"); return "sd1"; } }
從上面的程式碼我們可以看出,載入主方法時,先執行了輸出語句,然後是new D(),由此進行載入順序的分析。
執行結果如下:
/* * 1執行 ExaminationDemo 中的 main 函式, 建立 D 類例項 * 2執行 F 的建構函式 * 3執行 B 類的 static 塊(B 包含 E類的成員 變數,包含靜態 F 類成員變數) * 4執行 F 的函式 * 5執行 C 的 static 塊(C 繼承 B) * 6初始化 D 的 static 成員變數sd * 7執行 D 的 static 塊(D 繼承 C) * 8執行 E 的建構函式 * 9初始化 B 的例項成員變數 sb * 10執行 B 例項的普通初始化塊 * 11執行 B 類的建構函式(B 包含 E 類的成員變 量,包含靜態 F 類成員變數) * 13執行 C 的普通初始化塊 * 14執行 C 的建構函式(C 繼承B) * 15初始化 D 的例項成員變數 sd1 * 16執行 D 例項的普通初始化塊 * 17執行 D 的建構函式(D 繼承 C);父類 B 的實 例成員變數 sb的值為:sb;本類 D 的 static 成員變數 sd 的值為:sd; 本類 D 的例項成員變數 sd1 的值是:sd1 */
這個結果是怎麼來的,看下圖:
總體上還是按照二的載入順序來的,需要注意的是,當遇到子父類時,載入順序是:
父類的靜態成員變數,靜態程式碼塊——>子類的靜態成員變數,靜態程式碼塊——>父類的普通成員變數,匿名程式碼塊,構造器
——>子類的普通成員變數,匿名程式碼塊,構造器
靜態變數,和靜態程式碼塊的順序先後,主要看誰先寫,先寫的先載入
new物件和成員變數的順序,誰寫在前面就先載入誰(我理解的是兩者都屬於成員變數)
成員方法不呼叫就不載入
而,當成員變數是方法時,就要呼叫該方法。
無論是靜態成員方法,還是普通成員方法,只有在呼叫時才被載入
如果類還沒有被載入:
1、先執行父類的靜態程式碼塊和靜態變數初始化,並且靜態程式碼塊和靜態變數的執行順序只跟程式碼中出現的順序有關。
2、執行子類的靜態程式碼塊和靜態變數初始化。
3、執行父類的例項變數初始化
4、執行父類的建構函式
5、執行子類的例項變數初始化
6、執行子類的建構函式
如果類已經被載入:
則靜態程式碼塊和靜態變數就不用重複執行,再建立類物件時,只執行與例項相關的變數初始化和構造方法。