JVM類加載過程分爲幾個階段,分別是加載、驗證、準備、解析和初始化。加載是把二進制字節碼載入內存,驗證是校驗字節流中包含的信息是否符合當要求,準備是爲靜態變量分配內存並設置靜態變量初始值,解析是把常量池內的符號引用替換爲直接引用,初始化是執行全部靜態變量的賦值動做和靜態語句塊中的語句。更多詳盡分析請閱讀以前的文章《JVM的類加載機制全面解析》,這裏再也不贅述了。java
歡迎關注微信公衆號:萬貓學社,每週一分享Java技術乾貨。數組
對於咱們開發人員,我認爲應該具體瞭解一下初始化階段何時在開始。JVM規範對此作了嚴格規範,有且只有如下5種狀況必須對類進行初始化:微信
遇到new、getstatic、putstatic或invokestatic這四條字節碼指令時,若是類沒有被初始化過,就須要先進行初始化。對於字節碼指令不瞭解的同窗,可能就是一臉蒙圈了。咱們來講人話,就是:使用new關鍵字實例化對象的時候、讀取和設置一個類的靜態字段(不被final修飾的)和調用一個類的靜態方法的時候。這樣說更容易被理解一些。jvm
使用java.lang.reflect包中的方法對類進行反射調用的時候,若是類沒有被初始化過,就須要先進行初始化。spa
當初始化一個類的時候,若是發現它的父類尚未被初始化過,就須要先初始化它的父類。rest
JVM會先初始化要執行的主類,也是包含main()方法的那個類。code
當使用JDK 1.7的動態語言支持時,若是java.lang.invoke.MethodHandle實例最後的解析結果是REF_getStatic(使用MethodHandle讀取類的靜態字段)、REF_putStatic(使用MethodHandle設置類的靜態字段)、REF_invokeStatic(使用MethodHandle調用類的靜態方法)的方法句柄時,若是這個方法句柄沒有被初始化過,就須要先進行初始化。對象
歡迎關注微信公衆號:萬貓學社,每週一分享Java技術乾貨。繼承
剛剛提到的5種狀況,都會觸發初始化,這些行爲爲稱爲對一個類的主動引用。除了這些之外,全部引用類的方式都不會觸發初始化,被爲被動引用。爲了更好的理解,下面舉幾個被動引用的例子。接口
public class SuperClass { static { System.out.println("父類正在初始化"); } public static String name = "萬貓學社"; }
public class SubClass extends SuperClass { static { System.out.println("子類正在初始化"); } }
public class OneMoreStudy { public static void main(String[] args) { System.out.println(SubClass.name); } }
對於靜態變量,只有直接定義這個變量的類纔會被初始化,經過子類引用父類中定義的靜態變量,只會觸發父類的初始化而不會觸發子類的初始化,運行的結果是:
父類正在初始化 萬貓學社
結果中並無「子類正在初始化」。
歡迎關注微信公衆號:萬貓學社,每週一分享Java技術乾貨。
public class OneMoreStudy { public static void main(String[] args) { SuperClass[] arrays = new SuperClass[10]; System.out.println("數組元素個數:" + arrays.length); } }
這段代碼中使用以前的SuperClass類,定義了一個SuperClass類的一維數組,運行後的結果是:
數組元素個數:10
結果中並無「父類正在初始化」,說明並無觸發SuperClass類的初始化。實際上,有一個名爲「[LSuperClass」的類被初始化了,它是由JVM自動生成的、直接繼承於java.lang.Object,建立動做由字節碼指令newarray觸發。
歡迎關注微信公衆號:萬貓學社,每週一分享Java技術乾貨。
public class ConstClass { static { System.out.println("有常量的類正在初始化"); } public static final String NAME = "萬貓學社"; }
public class OneMoreStudy { public static void main(String[] args) { System.out.println(ConstClass.NAME); } }
常量在編譯階段會存入調用類的常量池中,本質沒有直接引用到定義的常量的類,不會觸發定義常量的類的初始化,因此運行的結果是:
萬貓學社
結果中並無「有常量的類正在初始化」。
歡迎關注微信公衆號:萬貓學社,每週一分享Java技術乾貨。
接口也有初始化過程,和類是一致的。不過接口中不能使用「static{}」語句塊,但編譯器仍然會爲接口生成「clinit()」類構造器,用於初始化接口中所定義的成員變量。
接口初始化的時機,基本和以前提到的類的5種狀況基本一致,惟一不同的是第3種狀況:在一個類被初始化時,它的父類也必須被初始化,可是一個接口被初始化時,它的父接口並不要求被初始化。只有在真正使用到父接口時纔會被初始化,好比:引用父接口中定義的常量。
歡迎關注微信公衆號:萬貓學社,每週一分享Java技術乾貨。
此次主要分享了類在何時被初始化,共有5種狀況。除了這種5種狀況的引用叫作被動引用,同時舉了3個被動引用的例子。同時,也提到初始化接口和類有什麼不一樣。
歡迎關注微信公衆號:萬貓學社,每週一分享Java技術乾貨。