九、 Java程式初始化的順序(二)
之前的一篇部落格裡我寫了關於在一個類中的程式初始化順序,但是在Java的物件導向裡,類之間還存在著繼承的關係。所以關於程式的初始化順序,我們可以再細劃分為:父類靜態變數,父類的靜態程式碼塊,父類構造器,父類非靜態變數,父類非靜態程式碼塊,子類靜態變數,子類靜態程式碼塊,子類構造器,子類非靜態成員變數和子類非靜態程式碼塊。 本篇部落格我們討論的就是關於程式初始化的過程中,上述的成員在初始化載入先後順序。 在此前我們討論得出的結論:在一個類中,Java程式載入的順序是:靜態變數-->靜態程式碼塊-->非靜態變數-->非靜態程式碼塊-->構造器. 父類的程式碼:
public class SuperClass { //父類與子類都在一個包中,這裡我們就使用default修飾符 //這是一個父類的靜態變數,此時還是初始化的預設值null static String superStaticVariale; // 靜態程式碼塊,給String賦值 static { superStaticVariale = "父類靜態程式碼塊賦值成功"; System.out.println("此時執行的是父類的靜態程式碼塊:"+superStaticVariale); } // 無參構造,覆蓋靜態程式碼塊的值 SuperClass(){ superStaticVariale = "父類構造器賦值成功"; System.out.println("此時執行的是父類的構造器:"+superStaticVariale); } //定義一個非靜態變數 String superVariale; // 定義一個非靜態程式碼塊 { superVariale = "父類非靜態程式碼塊賦值"; System.out.println("此時執行的是父類的非靜態程式碼塊:"+superVariale); } }
子類的程式碼:
public class SubClass extends SuperClass{ static String subStaticVariale; // 靜態程式碼塊,給String賦值 static { subStaticVariale = "子類靜態程式碼塊賦值成功"; System.out.println("此時執行的是子類的靜態程式碼塊:"+subStaticVariale); } // 無參構造,覆蓋靜態程式碼塊的值 SubClass(){ superStaticVariale = "子類構造器賦值成功"; System.out.println("此時執行的是子類的構造器:"+superStaticVariale); } //定義一個非靜態變數 String subVariale; // 定義一個非靜態程式碼塊 { subVariale = "子類非靜態程式碼塊賦值"; System.out.println("此時執行的是子類非靜態程式碼塊:"+subVariale); } }
測試程式碼:
public class Main { public static void main(String[] args) { SubClass s = new SubClass(); } }
執行結果: ``` 此時執行的是父類的靜態程式碼塊:父類靜態程式碼塊賦值成功 此時執行的是子類的靜態程式碼塊:子類靜態程式碼塊賦值成功 此時執行的是父類的非靜態程式碼塊:父類非靜態程式碼塊賦值 此時執行的是父類的構造器:父類構造器賦值成功 此時執行的是子類非靜態程式碼塊:子類非靜態程式碼塊賦值 此時執行的是子類的構造器:子類構造器賦值成功 ``` 很顯然,在繼承關係中,程式碼的載入順序是:父類的靜態變數-->父類的靜態程式碼塊-->子類靜態變數-->子類的靜態程式碼塊-->父類非靜態變數-->父類的非靜態程式碼塊-->父類的構造器-->子類非靜態變數-->子類非靜態程式碼塊-->子類構造器 進一步測試:
public class Main { public static void main(String[] args) { SubClass s = new SubClass(); SubClass s1 = new SubClass(); SubClass s2 = new SubClass(); } }
執行結果: ``` 此時執行的是父類的靜態程式碼塊:父類靜態程式碼塊賦值成功 此時執行的是子類的靜態程式碼塊:子類靜態程式碼塊賦值成功 此時執行的是父類的非靜態程式碼塊:父類非靜態程式碼塊賦值 此時執行的是父類的構造器:父類構造器賦值成功 此時執行的是子類非靜態程式碼塊:子類非靜態程式碼塊賦值 此時執行的是子類的構造器:子類構造器賦值成功 此時執行的是父類的非靜態程式碼塊:父類非靜態程式碼塊賦值 此時執行的是父類的構造器:父類構造器賦值成功 此時執行的是子類非靜態程式碼塊:子類非靜態程式碼塊賦值 此時執行的是子類的構造器:子類構造器賦值成功 此時執行的是父類的非靜態程式碼塊:父類非靜態程式碼塊賦值 此時執行的是父類的構造器:父類構造器賦值成功 此時執行的是子類非靜態程式碼塊:子類非靜態程式碼塊賦值 此時執行的是子類的構造器:子類構造器賦值成功 ``` 得出結論: 父類與子類的靜態程式碼都只執行一次,然後非靜態程式碼塊與構造器是組合出現的。 簡化一下程式碼:
public class Main { public static void main(String[] args) { C c= new C(); } }class A{ A(){ System.out.println("A的無參構造器"); } }class B extends A{// B(int a){ B(){ System.out.println("B的無參構造器"); } }class C extends B{ C(){ System.out.println("C的無參構造器"); } }
執行結果: ```text A的無參構造器 B的無參構造器 C的無參構造器 ``` 呼叫C的構造器生成C的例項物件會從最上級的父類的無參構造器開始逐層呼叫,那麼我們的類都繼承了一個超級父類Object,也就是在我們最初的錯誤程式碼中,我們呼叫Student的無參構造建立一個物件時,首先會呼叫這個物件的父類Object的無參構造器,
class Student{ String name; { name = "老大"; } Student(){ this(name);//這樣會報錯 super(); System.out.println("題目要求寫一個無參的構造器"); } Student(String name){ this.name = name; System.out.println(name); } }
子類例項化預設呼叫父類的無參構造器,也就是如上this呼叫在super()之前(實際中這兩者不會同時出現),name此時是非靜態屬性,此時會報錯錯誤: 無法在呼叫超型別構造器之前引用name。
class Student{ static String name; { name = "老大"; } Student(){ this(name); System.out.println("題目要求寫一個無參的構造器"); } Student(String name){ this.name = name; System.out.println(name); } }
當name是靜態屬性時,程式碼塊是非靜態時,編譯透過,呼叫子類的無參構造器時this(name),輸出結果是: ```text null 題目要求寫一個無參的構造器 ``` 此時的this()呼叫實參構造並沒有賦值成功。
class Student{ static String name; static{ name = "老大"; } Student(){ this(name); System.out.println("題目要求寫一個無參的構造器"); } Student(String name){ this.name = name; System.out.println(name); } }
此時執行結果: ```text 老大 題目要求寫一個無參的構造器 ``` 這樣賦值成功。由此證明我們的結論是正確的,this()是在子類父類構造器之前進行的操作super(),當子類程式碼塊是非靜態時,子類非靜態程式碼塊會在執行父類構造器之後執行,所以this(name)時name還沒有被賦值,所以列印是null。 結論: 1. 一個類中可以在無參構造器中呼叫此類的有參構造器(順序反過來); 2. 在執行子類的無參構造器時會預設呼叫最高階父類無參構造,並逐級呼叫直至子類的無參構造; 3. Java程式的載入順為父類的靜態變數-->父類的靜態程式碼塊-->子類靜態變數-->子類的靜態程式碼塊-->父類非靜態變數-->父類的非靜態程式碼塊-->父類的構造器-->子類非靜態變數-->子類非靜態程式碼塊-->子類構造器,且靜態變數或程式碼塊無論構造器呼叫多少次,他只會執行一次,後面再呼叫構造器則會執行非靜態屬性及程式碼塊構造器。 最後關於為什麼子類會呼叫父類的構造器,這個從設計著的角度來看是為了給從父類繼承的屬性初始化,子類需要知道父類是如何給屬性初始化的。
原文出處:https://www.cnblogs.com/Jeffding/p/9490510.html
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/430/viewspace-2812227/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- Java初始化順序Java
- java類初始化的順序Java
- JAVA物件的初始化順序Java物件
- Java 類初始化順序Java
- Java類初始化順序Java
- Java中class的初始化順序Java
- 關於java的初始化順序Java
- 程式碼說事 之 建立java物件的初始化順序Java物件
- Java類(繼承)初始化順序Java繼承
- 淺談Java類中的變數初始化順序Java變數
- Java初始化靜態變數的時間順序Java變數
- Java子類和父類的初始化執行順序Java
- c#物件初始化順序C#物件
- 總結類初始化時的程式碼執行順序
- Linux核心驅動程式初始化順序的調整(轉)Linux
- c#類的成員初始化順序C#
- static程式碼塊、構造程式碼塊、建構函式以及Java類初始化順序C程式函式Java
- 類的成員變數的初始化順序變數
- Java static變數、作用域、建構函式初始化順序Java變數函式
- 程式初始(二)——程式優先順序,環境變數變數
- java類內部程式碼執行順序Java
- C++建構函式初始化順序C++函式
- C++和C#物件初始化順序C++C#物件
- Java類的基本執行順序Java
- Java實現順序表Java
- java類載入順序Java
- Java的類的例項化順序Java
- 第九篇:順序容器及其常用函式函式
- 彙編學習小記(二)-順序程式設計程式設計
- 學Java,Java書籍的最佳閱讀順序Java
- python的順序程式設計Python程式設計
- Java父子類載入順序Java
- java運算子優先順序Java
- JAVA中取順序號 (轉)Java
- Java中,類與類,類中的程式碼執行順序Java
- 順序表實現二分排序排序
- JVM類載入機制與類初始化順序JVM
- java基礎(四) java運算順序的深入解析Java