1. 程式人生 > >Java類載入ClassLoader--雙親委託模型

Java類載入ClassLoader--雙親委託模型

Java類與類載入器

類載入器雖然只用於實現類的載入動作,但它在Java程式中起到的作用卻遠遠不限於類載入階段。對於任意一個類,都需要由載入它的類載入器和這個類本身一同確立其在Java虛擬機器中的唯一性,每一個類載入器,都擁有一個獨立的類名稱空間。比較兩個類是否“相等”,只有這兩個類是由同一個類載入器載入的前提下才有意義,否則,即使這兩個類來源於同一個Class檔案,被同一個虛擬機器載入,只要載入它們的類載入器不同,那這兩個類就必定不相等。這裡的“相等”包括代表類的Class物件的equals()方法、isAssignableFrom()方法,isInstance()方法的返回結果,也包括使用instanceof關鍵字做物件所屬關係判定等情況。

  • Java類載入ClassLoader

    1.什麼是ClassLoader?

         當我們寫好一個Java程式之後,不管是CS還是BS應用,都是由若干個.class檔案組織而成的一個完整的Java應用程式,當程式在執行時,即會呼叫該程式的一個入口函式來呼叫系統的相關功能,而這些功能都被封裝在不同的class檔案當中,所以經常要從這個class檔案中呼叫另外一個class檔案中的方法,如果另外一個檔案不存在的,則會引發系統異常。而程式在啟動的時候,並不會一次性載入程式所要用的所有class檔案,而是根據程式的需要,通過Java的類載入機制(ClassLoader)來動態載入某個class檔案到記憶體當中的,從而只有class檔案被載入到了記憶體之後,才能被其它class所引用。所以ClassLoader就是用來動態載入class檔案到記憶體當中用的。

2.Java預設提供的三個ClassLoader

1)BootStrap ClassLoader:稱為啟動類載入器,是Java類載入層次中最頂層的類載入器,負責載入JDK中的核心類庫,如:rt.jar、resources.jar、charsets.jar等,可通過如下程式獲得該類載入器從哪些地方載入了相關的jar或class檔案:

URL[] urls = sun.misc.Launcher.getBootstrapClassPath().getURLs();

for (int i = 0; i < urls.length; i++) {

    System.out.println(urls[i].toExternalForm());

}

2)Extension ClassLoader:稱為擴充套件類載入器,負責載入Java的擴充套件類庫,預設載入JAVA_HOME/jre/lib/ext/目下的所有jar。

3)App ClassLoader:稱為系統類載入器,負責載入應用程式classpath目錄下的所有jar和class檔案。

 注意: 除了Java預設提供的三個ClassLoader之外,使用者還可以根據需要定義自己的ClassLoader,而這些自定義的ClassLoader都必須繼承自java.lang.ClassLoader類,也包括Java提供的另外二個ClassLoader(Extension ClassLoader和App ClassLoader)在內,但是Bootstrap ClassLoader不繼承自ClassLoader,因為它不是一個普通的Java類,底層由C++編寫,已嵌入到了JVM核心當中,當JVM啟動後,Bootstrap ClassLoader也隨著啟動,負責載入完核心類庫後,並構造Extension ClassLoader和App ClassLoader類載入器。

3.ClassLoader載入類的原理

 1)原理介紹

       ClassLoader使用的是雙親委託模型來搜尋類的,每個ClassLoader例項都有一個父類載入器的引用(不是繼承的關係,是一個包含的關係),虛擬機器內建的類載入器(Bootstrap ClassLoader)本身沒有父類載入器,但可以用作其它ClassLoader例項的的父類載入器。當一個ClassLoader例項需要載入某個類時,它會試圖親自搜尋某個類之前,先把這個任務委託給它的父類載入器,這個過程是由上至下依次檢查的,首先由最頂層的類載入器Bootstrap ClassLoader試圖載入,如果沒載入到,則把任務轉交給Extension ClassLoader試圖載入,如果也沒載入到,則轉交給App ClassLoader 進行載入,如果它也沒有載入得到的話,則返回給委託的發起者,由它到指定的檔案系統或網路等URL中載入該類。如果它們都沒有載入到這個類時,則丟擲ClassNotFoundException異常。否則將這個找到的類生成一個類的定義,並將它載入到記憶體當中,最後返回這個類在記憶體中的Class例項物件。

2)為什麼要使用雙親委託這種模型呢?

       因為這樣可以避免重複載入,當父親已經載入了該類的時候,就沒有必要子ClassLoader再載入一次。考慮到安全因素,我們試想一下,如果不使用這種委託模式,那我們就可以隨時使用自定義的String來動態替代java核心api中定義的型別,這樣會存在非常大的安全隱患,而雙親委託的方式,就可以避免這種情況,因為String已經在啟動時就被引導類載入器(Bootstrcp ClassLoader)載入,所以使用者自定義的ClassLoader永遠也無法載入一個自己寫的String,除非你改變JDK中ClassLoader搜尋類的預設演算法。

3) 但是JVM在搜尋類的時候,又是如何判定兩個class是相同的呢?

JVM在判定兩個class是否相同時,不僅要判斷兩個類名是否相同,而且要判斷是否由同一個類載入器例項載入的。只有兩者同時滿足的情況下,JVM才認為這兩個class是相同的。

4.ClassLoad體系結構

Class類中有一個靜態方法forName,這個方法和ClassLoader中的loadClass方法目的一樣,都是用來載入class的,但兩者作用有所區別。使用forName載入的時候會將Class進行解釋和初始化。loadClass載入類時並不對該類進行解釋。