1. 程式人生 > >類加載之初始化(包括靜態代碼塊講解)

類加載之初始化(包括靜態代碼塊講解)

round ima left line title 耗時 觸發 靜態代碼塊 cnblogs

開始我們先來看一段代碼

package classLoader;

class a {
    public a() {
        System.out.println("init a");
    }
    
    public void say(){
        System.out.println("say a");
    }
    
    {
        System.out.println("block a");
    }
    
    static {
        System.out.println("static a
"); } } public class b extends a { public b() { System.out.println("init b"); } { System.out.println("block b"); } public void say(){ System.out.println("say b"); } static { System.out.println("
static b"); } public static void main(String[] args) { System.out.println("主程序開始!"); a test = new b(); //進行類的初始化 test.say(); System.out.println("重新創建對象!"); test = new b(); } }

運行結果:技術分享

我們可以看出靜態代碼優先於普通代碼快更優先與構造函數。同時靜態代碼塊在對象創建前就運行了靜態代碼塊,總是先運行父類代碼再運行子類代碼,我們也可以說父類代碼先行於子類代碼塊。而在給test重新賦予一個新的a對象時並沒有執行靜態代碼快。接下來我們來分析出現這些現象的原因。

首先我們先來聊聊類的初始化

java虛擬機對於5種情況必須進行初始化。

  1. 使用new關鍵字實例化對象嗎,讀取或設置一個靜態字段,以及調用一個靜態方法
  2. 進行反射調用的時候,如果沒有進行初始化先觸發其初始化。
  3. 初始化一個類時,發現其父類沒有進行初始化,則需要先進性父類的初始化。
  4. 虛擬機啟動需要執行一個主類時(包含main()方法的類)。
  5. 使用jdk1.7的動態語言支持時如果一個MethodHandle實例最後的解析結果REF_getstatic,REF_putstatic,REF_invokeStatic的句柄時,並且其沒有進行初始化則先出發其初始化

在準備階段進行系統要求的初始值賦值。而在初始化階段執行類構造器<clinit>()方法的過程。。

<clinit>()所發揮的作用

<clinit>()是初始化階段主要執行的代碼。根據程序員的要求進行變量的賦值和其他資源的調用。<clinit>()方法會自動收集所有類變量賦值和靜態代碼塊。該方法與構造函數不同,他不需要顯式的調用父類構造器。在執行子類的<clinit>()方法時父類的<clinit>()已經執行完畢。也就意味著父類的構造器先行於子類的構造器。所以會先輸出父類靜態代碼塊的內容,再輸出子類靜態代碼塊的內容。

註意:<clinit>()方法不是必須的,如果沒有靜態代碼,塊也沒有變量的賦值。那麽其可以不生成<clinit>()方法

<clinit>()是線程安全的,但如果一個縣城的<clinit>()耗時很長會導致多個進程阻塞。也就是說stati代碼塊應用不當會造成進程阻塞。

Q&A


在給test重新賦予一個新的a對象時並沒有執行靜態代碼快的原因?

該類已經初始化了,不用再進行初始化,故不需要進行初始化,也就不需要執行<clinit>(),自然也就不會輸出靜態代碼的內容。

靜態代碼塊,普通代碼塊,構造函數運行順序?

由程序運行結果可知:靜態代碼塊>普通代碼塊>構造函數

註意;父類代碼先行於子類代碼,代碼進行初始化5種情況的第3種

類加載之初始化(包括靜態代碼塊講解)