類加載之初始化(包括靜態代碼塊講解)
阿新 • • 發佈:2017-05-24
round ima left line title 耗時 觸發 靜態代碼塊 cnblogs
首先我們先來聊聊類的初始化
<clinit>()所發揮的作用
開始我們先來看一段代碼
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種情況必須進行初始化。
- 使用new關鍵字實例化對象嗎,讀取或設置一個靜態字段,以及調用一個靜態方法
- 進行反射調用的時候,如果沒有進行初始化先觸發其初始化。
- 初始化一個類時,發現其父類沒有進行初始化,則需要先進性父類的初始化。
- 虛擬機啟動需要執行一個主類時(包含main()方法的類)。
- 使用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種
類加載之初始化(包括靜態代碼塊講解)