一文看懂靜態初始化塊、靜態成員、初始化塊、建構函式執行順序以及用途
阿新 • • 發佈:2019-12-31
Tips
- 非靜態初始化塊基本和建構函式一個作用,可以避免建構函式的程式碼重複。初始化塊在類的每次構造都會執行
- 靜態初始化塊只在類載入執行一次
一個有意思的盲區:
執行psvm的時候,類的建構函式並不會執行,也就是說這時候類的例項並不存在。
public static void main(String[] args) {}
複製程式碼
測試執行順序
C extens B,B extends A , Main類中 new C()
public class InitLearn {
static class A{
static Print pA1 =new Print("static pA1" );
static {
System.out.println("Static Initialization Block A");
}
static Print pA2 =new Print("static pA2");
{
System.out.println("Initialization Block A Before Construct");
}
public A(){
System.out.println("Construct A" );
}
{
System.out.println("Initialization Block A After Construct");
}
};
static class B extends A{
static Print pB1 =new Print("static pB1");
static Print pB2 =new Print("static pB2");
static {
System.out.println("Static Initialization Block B" );
}
{
System.out.println("Initialization Block B Before Construct");
}
public B(){
System.out.println("Construct B");
}
{
System.out.println("Initialization Block B After Construct");
}
};
static class C extends B{
static Print pC1 =new Print("static pC1");
static {
System.out.println("Static Initialization Block C");
}
static Print pC2 =new Print("static pC2");
{
System.out.println("Initialization Block C Before Construct");
}
public C(){
System.out.println("Construct C");
}
{
System.out.println("Initialization Block C After Construct");
}
};
static class Print{
public Print(String v){
System.out.println(v);
}
}
static {
System.out.println("static{} of the class run psvm to test");
}
public static void main(String[] args) {
new C();
//new C();
}
}
複製程式碼
輸出:
static{} of the class run psvm to test //測試類的靜態初始化塊,在呼叫psvm前
static pA1 //A類靜態成員 定義在靜態初始化塊前
Static Initialization Block A //A類靜態初始化塊
static pA2 //A類靜態成員 定義在靜態初始化塊後
static pB1 //B類靜態成員 定義在靜態初始化塊前
static pB2 //B類靜態成員 定義在靜態初始化塊前
Static Initialization Block B //B類靜態初始化塊
static pC1 //C類靜態成員 定義在靜態初始化塊前
Static Initialization Block C //C類靜態初始化塊
static pC2 //C類靜態成員 定義在靜態初始化塊後
Initialization Block A Before Construct //A類初始化塊
Initialization Block A After Construct //A類初始化塊
Construct A //A類構造方法
Initialization Block B Before Construct
Initialization Block B After Construct
Construct B
Initialization Block C Before Construct
Initialization Block C After Construct
Construct C
複製程式碼
有趣的現象
-
InitLearn類是肯定沒有被例項化過的,但是由於執行main入口函式用到了InitLearn類,於是static初始化塊也被執行了;
-
所有的靜態初始化塊都優先執行,其次才是非靜態的初始化塊和建構函式,它們的執行順序是:
- 父類的靜態初始化塊/靜態成員,順序是宣告順序
- 子類的靜態初始化塊/靜態成員,順序是宣告順序
- 父類的初始化塊(無論定義在構造方法前還是後)
- 父類的建構函式
- 子類的初始化塊(無論定義在構造方法前還是後)
- 子類的建構函式
-
如果之後再new 一個B(),只會出現以下日誌,靜態的初始化過程只執行一次
Initialization Block A Before Construct Initialization Block A After Construct Construct A Initialization Block B Before Construct Initialization Block B After Construct Construct B Initialization Block C Before Construct Initialization Block C After Construct Construct C 複製程式碼
總結
- 靜態初始化塊的優先順序最高,也就是最先執行,並且僅在類第一次被載入時執行;
- 非靜態初始化塊和建構函式後執行,並且在每次生成物件時執行一次;
- 非靜態初始化塊的程式碼會在類建構函式之前執行。因此若要使用,應當養成把初始化塊寫在建構函式之前的習慣,便於除錯;
- 靜態初始化塊既可以用於初始化靜態成員變數,也可以執行初始化程式碼;
- 非靜態初始化塊可以針對多個過載建構函式進行程式碼複用。