Class.forName(className)與ClassLoader().loadClass(className)的區別

面試中會遇到"Class.forName(className)與ClassLoader().loadClass(className)的區別",如果需要知道答案,前提需要先了解到類的加載過程,如下圖:

1、加載

    類的加載階段,主要是獲取定義此類的二進制字節流,並將這個字節流所代表的靜態存儲結構轉化爲方法區的運行時數據結       構,最後在Java堆中生成一個代表這個類的java.lang.Class對象作爲方法區這些數據的訪問入口。相對於類加載過程的其他    階段,加載階段是開發期可控性最強的階段。我們可以通過定製不通的類加載器,也就是ClassLoader來控制二進制字節流的獲取方式。

2、驗證

    驗證,準備和解析其實都屬於連接階段,而驗證就是連接階段的第一步。這一階段主要是爲了確保Class文件的字節流中包含    的信息複合當前虛擬機的要求,並且不會危害虛擬機自身的安全。主要驗證過程包括:文件格式驗證,元數據驗證,字節碼驗    證以及符號引用驗證。 
   
3、準備

    準備階段正式爲類變量分配內存並設置初始值。這裏的初始值並不是初始化的值,而是數據類型的默認零值。這裏提到的類變量是被static修飾的變量,而不是實例變量。關於準備階段爲類變量設置零值的唯一例外就是當這個類變量同時也被final修飾,那麼在編譯時,就會直接爲這個常量賦上目標值。

4、解析

  解析時虛擬機將常量池中的符號引用替換爲直接引用的過程。

5、初始化

  在準備階段,變量已經賦過一次系統要求的初始值,在初始化階段,則是根據程序員通過程序的主觀計劃區初始化類變量和其   他資源。Java虛擬機規範規定了有4種情況必須立即對類進行初始化(加載,驗證,準備必須在此之前完成) 
  1)當使用new關鍵字實例化對象時,當讀取或者設置一個類的靜態字段(被final修飾的除外)時,以及當調用一個類的靜態     方法時(比如構造方法就是靜態方法),如果類未初始化,則需先初始化。 
  2)通過反射機制對類進行調用時,如果類未初始化,則需先初始化。 
  3)當初始化一個類時,如果其父類未初始化,先初始化父類。 
  4)用戶指定的執行主類(含main方法的那個類)在虛擬機啓動時會先被初始化。

 除了上面這4種方式,所有引用類的方式都不會觸發初始化,稱爲被動引用。如:通過子類引用父類的靜態字段,不會導致子類  初始化;通過數組定義來引用類,不會觸發此類的初始化;引用類的靜態常量不會觸發定義常量的類的初始化,因爲常量在編  譯階段已經被放到常量池中了。

那麼Class.forName(className)與ClassLoader().loadClass(className)的具體區別就是:

  • ClassLoader.loadClass(className)方法,內部實際調用的方法是  ClassLoader.loadClass(className,false);
  • 第2個 boolean參數,表示目標對象是否進行鏈接,false表示不進行鏈接,由上面介紹可以,不進行鏈接意味着不進行包括初始化等一些列步驟,那麼靜態塊和靜態對象就不會得到執行
  • ----------------------------------------------------------------------------------->
  • Class.forName(className)方法,內部實際調用的方法是  Class.forName(className,true,classloader);
  • 第2個boolean參數表示類是否需要初始化,  Class.forName(className)默認是需要初始化。
  • 一旦初始化,就會觸發目標對象的 static塊代碼執行,static參數也也會被再次初始化。

測試代碼如下:

classLoadTest.java
package classloader;

public class classLoadTest {
    static {
        System.out.println("this is a test!");
    }
}
Classloader.java
package classloader;

public class Classloader {
    public static void main(String[] args) throws ClassNotFoundException {
        
     /* 執行此註釋塊的代碼會打印出:this is a static code!
        try {
            Class.forName("classloader.classLoadTest");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }*/
        
        //執行如下代碼無任何輸出
        Classloader.class.getClassLoader().loadClass("classloader.classLoadTest");
    }
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章