靜態代碼塊:用staitc聲明,jvm載入類時執行,僅執行一次構造代碼塊:類中直接用{}定義,每一次創建對象時執行。執行順序優先順序:靜態塊,main(),構造塊,構造方法。 構造函數 關於構造函數,以下幾點要註意:1.對象一建立,就會調用與之相應的構造函數,也就是說,不建立對象,構造函數時不會運行的 ...
靜態代碼塊:用staitc聲明,jvm載入類時執行,僅執行一次
構造代碼塊:類中直接用{}定義,每一次創建對象時執行。
執行順序優先順序:靜態塊,main(),構造塊,構造方法。
構造函數
public HelloA(){//構造函數 }
關於構造函數,以下幾點要註意:
1.對象一建立,就會調用與之相應的構造函數,也就是說,不建立對象,構造函數時不會運行的。
2.構造函數的作用是用於給對象進行初始化。
3.一個對象建立,構造函數只運行一次,而一般方法可以被該對象調用多次。
構造代碼塊
{//構造代碼塊 }
關於構造代碼塊,以下幾點要註意:
- 構造代碼塊的作用是給對象進行初始化。
- 對象一建立就運行構造代碼塊了,而且優先於構造函數執行。這裡要強調一下,有對象建立,才會運行構造代碼塊,類不能調用構造代碼塊的,而且構造代碼塊與構造函數的執行順序是前者先於後者執行。
- 構造代碼塊與構造函數的區別是:構造代碼塊是給所有對象進行統一初始化,而構造函數是給對應的對象初始化,因為構造函數是可以多個的,運行哪個構造函數就會建立什麼樣的對象,但無論建立哪個對象,都會先執行相同的構造代碼塊。也就是說,構造代碼塊中定義的是不同對象共性的初始化內容。
靜態代碼塊
static {//靜態代碼塊 }
關於靜態代碼塊,要註意的是:
- 它是隨著類的載入而執行,只執行一次,並優先於主函數。具體說,靜態代碼塊是由類調用的。類調用時,先執行靜態代碼塊,然後才執行主函數的。
- 靜態代碼塊其實就是給類初始化的,而構造代碼塊是給對象初始化的。
- 靜態代碼塊中的變數是局部變數,與普通函數中的局部變數性質沒有區別。
- 一個類中可以有多個靜態代碼塊
public class Test{ staitc int cnt=6; static{ cnt+=9; } public static void main(String[] args) { System.out.println(cnt); } static{ cnt/=3; } } 運行結果: 5
Java類初始化順序
## 對於一個類的情況
例子1:
public class HelloA { public HelloA(){//構造函數 System.out.println("A的構造函數"); } {//構造代碼塊 System.out.println("A的構造代碼塊"); } static {//靜態代碼塊 System.out.println("A的靜態代碼塊"); } public static void main(String[] args) { } } 運行結果: A的靜態代碼塊
例子2:
public class HelloA { public HelloA(){//構造函數 System.out.println("A的構造函數"); } {//構造代碼塊 System.out.println("A的構造代碼塊"); } static {//靜態代碼塊 System.out.println("A的靜態代碼塊"); } public static void main(String[] args) { HelloA a=new HelloA(); } } 運行結果: A的靜態代碼塊 A的構造代碼塊 A的構造函數
例子3:
public class HelloA { public HelloA(){//構造函數 System.out.println("A的構造函數"); } {//構造代碼塊 System.out.println("A的構造代碼塊"); } static {//靜態代碼塊 System.out.println("A的靜態代碼塊"); } public static void main(String[] args) { HelloA a=new HelloA(); HelloA b=new HelloA(); } } 運行結果: A的靜態代碼塊 A的構造代碼塊 A的構造函數 A的構造代碼塊 A的構造函數
對於一個類而言,按照如下順序執行:
- 執行靜態代碼塊
- 執行構造代碼塊
- 執行構造函數
對於靜態變數、靜態初始化塊、變數、初始化塊、構造器,它們的初始化順序依次是(靜態變數、靜態初始化塊)>(變數、初始化塊)>構造器。
例子4:
1 public class InitialOrderTest { 2 /* 靜態變數 */ 3 public static String staticField = "靜態變數"; 4 /* 變數 */ 5 public String field = "變數"; 6 /* 靜態初始化塊 */ 7 static { 8 System.out.println( staticField ); 9 System.out.println( "靜態初始化塊" ); 10 } 11 /* 初始化塊 */ 12 { 13 System.out.println( field ); 14 System.out.println( "初始化塊" ); 15 } 16 /* 構造器 */ 17 public InitialOrderTest() 18 { 19 System.out.println( "構造器" ); 20 } 21 22 23 public static void main( String[] args ) 24 { 25 new InitialOrderTest(); 26 } 27 }
運行以上代碼,我們會得到如下的輸出結果:
-
靜態變數
-
靜態初始化塊
-
變數
-
初始化塊
-
構造器
## 對於繼承情況
例子5:
public class HelloA { public HelloA(){//構造函數 System.out.println("A的構造函數"); } {//構造代碼塊 System.out.println("A的構造代碼塊"); } static {//靜態代碼塊 System.out.println("A的靜態代碼塊"); } } public class HelloB extends HelloA{ public HelloB(){//構造函數 System.out.println("B的構造函數"); } {//構造代碼塊 System.out.println("B的構造代碼塊"); } static {//靜態代碼塊 System.out.println("B的靜態代碼塊"); } public static void main(String[] args) { HelloB b=new HelloB(); } } 運行結果: A的靜態代碼塊 B的靜態代碼塊 A的構造代碼塊 A的構造函數 B的構造代碼塊 B的構造函數
當涉及到繼承時,按照如下順序執行:
- 執行父類的靜態代碼塊,並初始化父類靜態成員變數
- 執行子類的靜態代碼塊,並初始化子類靜態成員變數
- 執行父類的構造代碼塊,執行父類的構造函數,並初始化父類普通成員變數
- 執行子類的構造代碼塊, 執行子類的構造函數,並初始化子類普通成員變數
例子6:
1 class Parent { 2 /* 靜態變數 */ 3 public static String p_StaticField = "父類--靜態變數"; 4 /* 變數 */ 5 public String p_Field = "父類--變數"; 6 protected int i = 9; 7 protected int j = 0; 8 /* 靜態初始化塊 */ 9 static { 10 System.out.println( p_StaticField ); 11 System.out.println( "父類--靜態初始化塊" ); 12 } 13 /* 初始化塊 */ 14 { 15 System.out.println( p_Field ); 16 System.out.println( "父類--初始化塊" ); 17 } 18 /* 構造器 */ 19 public Parent() 20 { 21 System.out.println( "父類--構造器" ); 22 System.out.println( "i=" + i + ", j=" + j ); 23 j = 20; 24 } 25 } 26 27 public class SubClass extends Parent { 28 /* 靜態變數 */ 29 public static String s_StaticField = "子類--靜態變數"; 30 /* 變數 */ 31 public String s_Field = "子類--變數"; 32 /* 靜態初始化塊 */ 33 static { 34 System.out.println( s_StaticField ); 35 System.out.println( "子類--靜態初始化塊" ); 36 } 37 /* 初始化塊 */ 38 { 39 System.out.println( s_Field ); 40 System.out.println( "子類--初始化塊" ); 41 } 42 /* 構造器 */ 43 public SubClass() 44 { 45 System.out.println( "子類--構造器" ); 46 System.out.println( "i=" + i + ",j=" + j ); 47 } 48 49 50 /* 程式入口 */ 51 public static void main( String[] args ) 52 { 53 System.out.println( "子類main方法" ); 54 new SubClass(); 55 } 56 }
結果:
父類--靜態變數
父類--靜態初始化塊
子類--靜態變數
子類--靜態初始化塊
子類main方法
父類--變數
父類--初始化塊
父類--構造器
i=9, j=0
子類--變數
子類--初始化塊
子類--構造器
i=9,j=20
子類的靜態變數和靜態初始化塊的初始化是在父類的變數、初始化塊和構造器初始化之前就完成了。靜態變數、靜態初始化塊,變數、初始化塊初始化了順序取決於它們在類中出現的先後順序。
### 分析
-
(1)訪問SubClass.main(),(這是一個static方法),於是裝載器就會為你尋找已經編譯的SubClass類的代碼(也就是SubClass.class文件)。在裝載的過程中,裝載器註意到它有一個基類(也就是extends所要表示的意思),於是它再裝載基類。不管你創不創建基類對象,這個過程總會發生。如果基類還有基類,那麼第二個基類也會被裝載,依此類推。
-
(2)執行根基類的static初始化,然後是下一個派生類的static初始化,依此類推。這個順序非常重要,因為派生類的“static初始化”有可能要依賴基類成員的正確初始化。
-
(3)當所有必要的類都已經裝載結束,開始執行main()方法體,並用new SubClass()創建對象。
-
(4)類SubClass存在父類,則調用父類的構造函數,你可以使用super來指定調用哪個構造函數。基類的構造過程以及構造順序,同派生類的相同。首先基類中各個變數按照字面順序進行初始化,然後執行基類的構造函數的其餘部分。
-
(5)對子類成員數據按照它們聲明的順序初始化,執行子類構造函數的其餘部分。
參考文章鏈接: