什麼狀況下會觸發類加載的進行呢?本文將結合代碼demo談談幾種狀況,但願對你們有幫助。面試
什麼狀況須要開始類加載過程的第一階段:加載?Java虛擬機規範中並無進行強制約束,這點能夠交給虛擬機的具體實現來自由把握。可是對於初始化階段,虛擬機規範則嚴格規定了如下幾種狀況必須當即對類進行初始化,若是類沒有進行過初始化,則須要先觸發其初始化。bash
爲了驗證類加載,咱們先配置一個JVM參數函數
-XX:+TraceClassLoading 監控類的加載
複製代碼
在IDE配置以下:學習
demo代碼:ui
public class ClassLoadInstance {
static {
System.out.println("ClassLoadInstance類初始化時就會被執行!");
}
public ClassLoadInstance() {
System.out.println("ClassLoadInstance構造函數!");
}
}
public class ClassLoadTest {
public static void main(String[] args) {
ClassLoadInstance instance = new ClassLoadInstance();
}
}
複製代碼
運行結果:spa
結論:3d
new ClassLoadInstance實例時,發現ClassLoadInstance被加載了,所以 new建立實例對象,會觸發類加載進行。code
demo代碼:cdn
public class ClassLoadStaticVariable {
static {
System.out.println("ClassLoadStaticVariable類初始化時就會被執行!");
}
public static int i = 100;
public ClassLoadStaticVariable() {
System.out.println("ClassLoadStaticVariable構造函數!");
}
}
public class ClassLoadTest {
public static void main(String[] args) {
System.out.println(ClassLoadStaticVariable.i);
}
}
複製代碼
運行結果:對象
結論:
訪問類ClassLoadStaticVariable的靜態變量i時,發現ClassLoadStaticVariable類被加載啦,所以訪問類的靜態變量會觸發類加載。
注意:
訪問final修飾的靜態變量時,不會觸發類加載,由於在編譯期已經將此常量放在常量池了。
demo代碼:
public class ClassLoadStaticMethod {
static {
System.out.println("ClassLoadStaticMethod類初始化時就會被執行!");
}
public static void method(){
System.out.println("靜態方法被調用");
}
public ClassLoadStaticMethod() {
System.out.println("ClassLoadStaticMethod構造函數!");
}
}
public class ClassLoadTest {
public static void main(String[] args) {
ClassLoadStaticMethod.method();
}
}
複製代碼
運行結果:
結論:
訪問類ClassLoadStaticMethod的靜態方法method時,發現ClassLoadStaticMethod類被加載啦,所以訪問類的靜態方法會觸發類加載。
demo代碼:
package classload;
public class ClassLoadStaticReflect {
static {
System.out.println("ClassLoadStaticReflect類初始化時就會被執行!");
}
public static void method(){
System.out.println("靜態方法被調用");
}
public ClassLoadStaticReflect() {
System.out.println("ClassLoadStaticReflect構造函數!");
}
}
public class ClassLoadTest {
public static void main(String[] args) throws ClassNotFoundException {
Class.forName("classload.ClassLoadStaticReflect");
}
}
複製代碼
運行結果:
結論:
反射獲得類ClassLoadStaticReflect時,發現ClassLoadStaticReflect類被加載啦,所以反射會觸發類加載。
demo代碼:
//父類
public class ClassLoadSuper {
static {
System.out.println("ClassLoadSuper類初始化時就會被執行!這是父類");
}
public static int superNum = 100;
public ClassLoadSuper() {
System.out.println("父類ClassLoadSuper構造函數!");
}
}
//子類
public class ClassLoadSub extends ClassLoadSuper {
static {
System.out.println("ClassLoadSub類初始化時就會被執行!這是子類");
}
public static int subNum = 100;
public ClassLoadSub() {
System.out.println("子類ClassLoadSub構造函數!");
}
}
public class ClassLoadTest {
public static void main(String[] args) throws ClassNotFoundException {
ClassLoadSub classLoadSub = new ClassLoadSub();
}
}
複製代碼
運行結果:
看了運行結果,是否是發現,網上那道經典面試題( 講講類的實例化順序?)也很清晰啦。 先父類靜態變量/靜態代碼塊-> 再子類靜態變量/靜態代碼塊->父類構造器->子類構造器結論:
實例化子類ClassLoadSub的時候,發現父類ClassLoadSuper先被加載,所以當初始化一個類時,發現其父類還未初始化,則先觸發父類的初始化
demo代碼:
package classload;
public class ClassLoadTest {
public static void main(String[] args) {
System.out.println(ClassLoadSub.subNum);
}
}
複製代碼
運行結果:
結論:
虛擬機啓動時,即便有ClassLoadSub,ClassLoadSuper,ClassLoadTest等類被加載, 但ClassLoadTest最早被加載,即定義了main()方法的那個類會先觸發類加載。
觸發類加載的六大時機,咱們都分析完啦,是否是不作個題都以爲意猶未盡呢?接下來,咱們來分析類加載一道經典面試題吧。
class SingleTon {
private static SingleTon singleTon = new SingleTon();
public static int count1;
public static int count2 = 0;
private SingleTon() {
count1++;
count2++;
}
public static SingleTon getInstance() {
return singleTon;
}
}
public class ClassLoadTest {
public static void main(String[] args) {
SingleTon singleTon = SingleTon.getInstance();
System.out.println("count1=" + singleTon.count1);
System.out.println("count2=" + singleTon.count2);
}
}
複製代碼
運行結果:
分析:
歡迎你們關注,你們一塊兒學習,一塊兒討論。