Java提高篇——靜態代碼塊、構造代碼塊、構造函數以及Java類初始化順序
閱讀目錄
靜態代碼塊:用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:
運行以上代碼,我們會得到如下的輸出結果:
靜態變量
靜態初始化塊
變量
初始化塊
構造器
運行結果:
A的靜態代碼塊
B的靜態代碼塊
A的構造代碼塊
A的構造函數
B的構造代碼塊
B的構造函數
當涉及到繼承時,按照如下順序執行:
1. 執行父類的靜態代碼塊,并初始化父類靜態成員變量
2. 執行子類的靜態代碼塊,并初始化子類靜態成員變量
3. 執行父類的構造代碼塊,執行父類的構造函數,并初始化父類普通成員變量
4. 執行子類的構造代碼塊, 執行子類的構造函數,并初始化子類普通成員變量
結果:
父類--靜態變量
父類--靜態初始化塊
子類--靜態變量
子類--靜態初始化塊
子類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)對子類成員數據按照它們聲明的順序初始化,執行子類構造函數的其余部分。