今天先來分析一下經常遇到的一個問題,在筆試面試中可能會經常遇見,類中字段代碼塊的加載順序等,從jvm角度分析一下這個問題。我們先來看下知識點,接下來進行代碼實踐驗證。
- <clinit>,類構造器方法,在jvm第一次加載class文件時調用,因爲是類級別的,所以只加載一次,是編譯器自動收集類中所有類變量(static修飾的變量)和靜態語句塊(static{}),中的語句合併產生的,編譯器收集的順序,是由程序員在寫在源文件中的代碼的順序決定的。
- <init>,實例構造器方法,在實例創建出來的時候調用,包括調用new操作符;調用Class或java.lang.reflect.Constructor對象的newInstance()方法;調用任何現有對象的clone()方法;通過java.io.ObjectInputStream類的getObject()方法反序列化。
-
邊驗證,邊解析
public class Main {
//實例化代碼塊.每次生成類實例,都會執行.並且,實例化代碼塊的執行 優先於 構造器.
{
System.out.println("blockA");
}
//靜態語句塊,在類初始化時,僅僅執行一次.
static{
System.out.println("blockB");
}
//類實例
public static Main t1 = new Main();
//構造器方法
Main(){
System.out.println("constructor");
}
public static void main(String[] args)
{
//類實例
Main t2 = new Main();
}
}
輸出結果
blockB
blockA
constructor
blockA
constructor
結果分析: 首先在Main類調用main方法,符合虛擬機規定的5種立即進行初始化。初始化的過程就是執行類構造器方法的<clinit>過程,首先初始化static語句,接着初始化類變量t1,執行t1的實例代碼塊,t1構造器方法<init>,最後在執行main方法中t2實例化。在此過程中,<clinit>只有在class文件第一次被加載時執行,<init>每次實例化時都會被執行一次。
通俗易懂解釋就是,首先需要<clinit>完成類級別的變量和代碼塊的加載,在進行對象級別的加載信息。
-
需要記住的幾點
1.<clinit>方法和類的構造函數不同,它不需要顯示調用父類的構造方法,虛擬機會保證子類的<clinit>方法執行之前,父類的此方法已經執行完畢,因此虛擬機中第一個被執行的<clinit>方法的類肯定是java.lang.Object
2、接口中不能使用static塊,但是接口仍然有變量初始化的操作,因此接口也會生成<clinit>方法。但接口和類不同的是,不會先去執行繼承接口的<clinit>方法,而是在調用父類變量的時候,纔會去調用<clinit>方法。接口的實現類也是一樣的。