迴歸Java基礎:觸發類加載的六大時機

前言

什麼狀況下會觸發類加載的進行呢?本文將結合代碼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先被加載,所以當初始化一個類時,發現其父類還未初始化,則先觸發父類的初始化

虛擬機啓動時,定義了main()方法的那個類先初始化

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);  
    }  
}  

複製代碼

運行結果:

分析:

  1. SingleTon.getInstance(),調用靜態方法,觸發SingleTon類加載。
  2. SingleTon類加載初始化,按順序初始化靜態變量。
  3. 先執行private static SingleTon singleTon = new SingleTon(); ,調用構造器後,count1,count2均爲1;
  4. 按順序執行 public static int count1; 沒有賦值,因此count1依舊爲1;
  5. 按順序執行 public static int count2 = 0;因此count2變爲0.

我的公衆號

歡迎你們關注,你們一塊兒學習,一塊兒討論。

相關文章
相關標籤/搜索