大部分教程都會告訴我們靜態初始化塊和靜態欄位總是在初始化塊和普通類欄位前運行,事實上也確實如此,直到我看到下面這樣的代碼:
public class Test {
static Test test = new Test();
{
System.out.println("normal");
}
static{
System.out.println("static");
}
public static void main(String [] args){
Test test = new Test();
}
}
可以先猜一猜結果,然后我們編譯運行:
$ javac Test.java
$ java Test
normal
static
normal
難道不應該是先列印出static才對嗎?這里我就不賣關子了,答案很簡單,我們先來看一段話:
every constructor written in the Java programming language (JLS §8.8) appears as an instance initialization method that has the special name <init>. The initialization method of a class or interface has the special name <clinit>.
這段話來自jls,意思是普通的建構式和初始化塊會被放在一個叫<init>的方法里,在創建物件實體時呼叫;而類自身的初始化包括靜態欄位和靜態方法會被放在叫<clinit>的方法里,而靜態欄位和靜態塊的初始化優先級相同,順序遵從在代碼中排列的順序,也就是誰在前面誰先執行,
因此執行順序已經明了了:
- 因為main函式中要創建類的實體,所以類會先被加載,這是呼叫了
<clinit> - 靜態欄位在靜態初始化塊之前,所以先執行初始化
- 靜態欄位呼叫了
new Test()創建類的實體,這時呼叫了<init> - 因為普通的初始化塊在
<init>里,所以被呼叫,首先列印出了normal - 靜態欄位初始化完成后開始執行緊隨其后的靜態塊,列印出static
- 隨后回到main函式中,類的初始化只會運行一次,所以這次只有
<init>運行,列印出normal
因此看起來像是普通初始化塊在靜態塊之前運行了,實際上只是靜態欄位的初始化導致了普通初始化塊的提前執行,
知道了原理后,想下面這樣的代碼會輸出什么自然也不在話下了:
public class Init {
private int v = 0;
static Init obj2 = new Init(); // 只能是static欄位,想一想為什么
static {
System.out.println("static 2");
}
static Init obj1 = new Init();
{
System.out.println("from init");
}
static {
System.out.println("static 1");
}
public static void main(String[] args) {
var o1 = new Init();
var o2 = new Init();
}
}
當然,在實際的編碼中靜態塊應該只負責靜態欄位的處理,普通的初始化塊只負責對普通類欄位的處理,上面的代碼是不應該被模仿的,
參考
https://www.zhihu.com/question/50374553
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/3013.html
標籤:Java
