虛擬機類加載機制之類加載時機(JVM學習筆記)

類聲明週期

類從被加載到虛擬機內存開始,到卸載出內存爲止,他生命週期包括了:

加載->驗證->準備->解析->初始化->使用->卸載

什麼情況下開始類加載第一階段?

  1. 遇到new,getstatic,putstatic或者invokestatic這4條字節碼指令時。如果類沒有驚醒過初始化,則先觸發其初始化。常見場景:new實例化對象,讀取或者設置一個類的靜態字段(被final修飾,已在編譯期間把結果放入常量池的靜態字段除外)的時候,以及調用靜態方法。
  2. 使用java.lang.reflect包的方法對類進行反射調用的時候,如果類沒有進行初始化,則需先觸發初始化。
  3. 當初始化一個類,如果發現其父類還沒有進行過初始化,則先出法其父類的初始化。
  4. 當虛擬機啓動時,用戶需要指定一個需要執行的主類,虛擬機會初始化這個主類。

    這四種場景的行爲成爲對一個類的主動引用,除了以上所用引用類的方式都不會觸發初始化,稱爲被動引用。

被動引用示例1:

package classloading;

public class SuperClass {
    static {
        System.out.println("classloading.SuperClass init!");
    }

    public static int value = 123;
}
package classloading;

public class SubClass extends SuperClass {
    static {
        System.out.println("subclass init!");
    }
}
package classloading;
public class NotInitialization {
    public static void main(String[] args) {
        System.out.println(SubClass.value);
    }
}

輸出結果:

classloading.SuperClass init!

123

對於靜態字段只有直接定義這個字段的類纔會被初始化,因此通過子類引用父類定義的靜態字段並不會觸發子類初始化。至於是否要觸發取決於虛擬機的具體實現。

被動引用示例2:

package classloading;
public class NotInitialization {
    public static void main(String[] args) {
        SuperClass[] sca=new SuperClass[10];
    }
}

運行後發現並沒有觸發任何子類或者父類初始化。

被動引用示例3:

package classloading;
public class ConstantClass {
    static {
        System.out.println("ConstantClass init!!");
    }
    public static final String HELLOWORLD = "hello world!";
}
package classloading;
public class NotInitialization {
    public static void main(String[] args) {
        System.out.println(ConstantClass.HELLOWORLD);
    }
}

運行後發現並未打印出ConstantClass init!!,這是因爲java源碼在編譯階段將次常量的值存儲到了NotInitialization的常量池中,對常量ConstantClass.HELLOWORLD的引用實際上傳化爲了對自身常量池的引用了。

接口初始化:

接口與類初始化區別:類在書初始化的時候需要其父類全部初始化,但是接口在初始化時候,並不需要父接口全部完成初始化,只有真正使用到父類接口的時候纔會初始化。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章