Java虛擬機類加載過程是把Class類文件加載到內存,並對Class文件中的數據進行校驗、轉換解析和初始化,最終造成能夠被虛擬機直接使用的java類型的過程。java
在加載階段,java虛擬機須要完成如下3件事:數據結構
a.經過一個類的全限定名來獲取定義此類的二進制字節流。spa
b.將定義類的二進制字節流所表明的靜態存儲結構轉換爲方法區的運行時數據結構。.net
c.在java堆中生成一個表明該類的java.lang.Class對象,做爲方法區數據的訪問入口。線程
Java虛擬機的類加載是經過類加載器實現的, Java中的類加載器體系結構以下:對象
(1).BootStrap ClassLoader:啓動類加載器,負責加載存放在%JAVA_HOME%\lib目錄中的,或者通被-Xbootclasspath參數所指定的路徑中的,而且被java虛擬機識別的(僅按照文件名識別,如rt.jar,名字不符合的類庫,即便放在指定路徑中也不會被加載)類庫到虛擬機的內存中,啓動類加載器沒法被java程序直接引用。blog
(2).Extension ClassLoader:擴展類加載器,由sun.misc.Launcher$ExtClassLoader實現,負責加載%JAVA_HOME%\lib\ext目錄中的,或者被java.ext.dirs系統變量所指定的路徑中的全部類庫,開發者能夠直接使用擴展類加載器。繼承
(3).Application ClassLoader:應用程序類加載器,由sun.misc.Launcher$AppClassLoader實現,負責加載用戶類路徑classpath上所指定的類庫,是類加載器ClassLoader中的getSystemClassLoader()方法的返回值,開發者能夠直接使用應用程序類加載器,若是程序中沒有自定義過類加載器,該加載器就是程序中默認的類加載器。接口
注意:上述三個JDK提供的類加載器雖然是父子類加載器關係,可是沒有使用繼承,而是使用了組合關係。ip
從JDK1.2開始,java虛擬機規範推薦開發者使用雙親委派模式(ParentsDelegation Model)進行類加載,其加載以下:
(1).若是一個類加載器收到了類加載請求,它首先不會本身去嘗試加載這個類,而是把類加載請求委派給父類加過程載器去完成。
(2).每一層的類加載器都把類加載請求委派給父類加載器,直到全部的類加載請求都應該傳遞給頂層的啓動類加載器。
(3).若是頂層的啓動類加載器沒法完成加載請求,子類加載器嘗試去加載,若是連最初發起類加載請求的類加載器也沒法完成加載請求時,將會拋出ClassNotFoundException,而再也不調用其子類加載器去進行類加載。
雙親委派 模式的類加載機制的優勢是java類它的類加載器一塊兒具有了一種帶優先級的層次關係,越是基礎的類,越是被上層的類加載器進行加載,保證了java程序的穩定運行。雙親委派模式的實現:
若要實現自定義類加載器,只須要繼承java.lang.ClassLoader 類,而且重寫其findClass()方法便可。java.lang.ClassLoader 類的基本職責就是根據一個指定的類的名稱,找到或者生成其對應的字節代碼,而後從這些字節代碼中定義出一個 Java 類,即 java.lang.Class 類的一個實例。除此以外,ClassLoader 還負責加載 Java 應用所需的資源,如圖像文件和配置文件等,ClassLoader 中與加載類相關的方法以下:
方法 |
說明 |
getParent() |
返回該類加載器的父類加載器。 |
loadClass(String name) |
加載名稱爲 二進制名稱爲name 的類,返回的結果是 java.lang.Class 類的實例。
|
findClass(String name) |
查找名稱爲 name 的類,返回的結果是 java.lang.Class 類的實例。
|
findLoadedClass(String name) |
查找名稱爲 name 的已經被加載過的類,返回的結果是 java.lang.Class 類的實例。
|
resolveClass(Class<?> c) |
連接指定的 Java 類。 |
注意:在JDK1.2以前,類加載還沒有引入雙親委派模式,所以實現自定義類加載器時經常重寫loadClass方法,提供雙親委派邏輯,從JDK1.2以後,雙親委派模式已經被引入到類加載體系中,自定義類加載器時不須要在本身寫雙親委派的邏輯,所以不鼓勵重寫loadClass方法,而推薦重寫findClass方法。
在Java中,任意一個類都須要由加載它的類加載器和這個類自己一同肯定其在java虛擬機中的惟一性,即比較兩個類是否相等,只有在這兩個類是由同一個類加載器加載的前提之下才有意義,不然,即便這兩個類來源於同一個Class類文件,只要加載它的類加載器不相同,那麼這兩個類一定不相等(這裏的相等包括表明類的Class對象的equals()方法、isAssignableFrom()方法、isInstance()方法和instanceof關鍵字的結果)。例子代碼以下:
輸出結果以下:
com.test.ClassLoaderTest
false
之因此instanceof會返回false,是由於com.test.ClassLoaderTest類默認使用Application ClassLoader加載,而obj是經過自定義類加載器加載的,類加載不相同,所以不相等。
類加載器雙親委派模型是從JDK1.2之後引入的,而且只是一種推薦的模型,不是強制要求的,所以有一些沒有遵循雙親委派模型的特例:
(1).在JDK1.2以前,自定義類加載器都要覆蓋loadClass方法去實現加載類的功能,JDK1.2引入雙親委派模型以後,loadClass方法用於委派父類加載器進行類加載,只有父類加載器沒法完成類加載請求時才調用本身的findClass方法進行類加載,所以在JDK1.2以前的類加載的loadClass方法沒有遵循雙親委派模型,所以在JDK1.2以後,自定義類加載器不推薦覆蓋loadClass方法,而只須要覆蓋findClass方法便可。
(2).雙親委派模式很好地解決了各個類加載器的基礎類統一問題,越基礎的類由越上層的類加載器進行加載,可是這個基礎類統一有一個不足,當基礎類想要調用回下層的用戶代碼時沒法委派子類加載器進行類加載。爲了解決這個問題JDK引入了ThreadContext線程上下文,經過線程上下文的setContextClassLoader方法能夠設置線程上下文類加載器。
JavaEE只是一個規範,sun公司只給出了接口規範,具體的實現由各個廠商進行實現,所以JNDI,JDBC,JAXB等這些第三方的實現庫就能夠被JDK的類庫所調用。線程上下文類加載器也沒有遵循雙親委派模型。
(3).近年來的熱碼替換,模塊熱部署等應用要求不用重啓java虛擬機就能夠實現代碼模塊的即插即用,催生了OSGi技術,在OSGi中類加載器體系被髮展爲網狀結構。OSGi也沒有徹底遵循雙親委派模型。