Java 類加載器加載機(jī)制

問:簡單說說 java 類加載器的理解及加載機(jī)制?

答:通過 java 命令運行 java 程序的步驟就是指定包含 main 方法的完整類名以及一個 classpath 類路徑,類路徑可以有多個,對于直接的 class 文件路徑就是 class 文件的根目錄,對于 jar 包文件路徑是 jar 包的完整路徑,包含 jar 包名字;java 運行時會根據(jù)類的完全限定名尋找并加載,尋找的方式基本就是在系統(tǒng)類和指定的路徑中尋找,如果是 class 文件的根目錄則直接查看是否有對應(yīng)的子目錄及文件,如果是 jar 包則首先在內(nèi)存中解壓文件,然后再查看是否有對應(yīng)的類;負(fù)責(zé)類加載的類就是 ClassLoader 類加載器,它的輸入是完全限定的類名,輸出是 Class 對象。

java 虛擬機(jī)中可以安裝多個類加載器,系統(tǒng)默認(rèn)主要有三個類加載器,每個類負(fù)責(zé)加載特定位置的類,也可以自定義類加載器,自定義的加載器必須繼承 ClassLoader,如下:

  • 啟動類加載器(Bootstrap ClassLoader):此加載器為虛擬機(jī)實現(xiàn)的一部分,不是 java 語言上層實現(xiàn)的,一般為 C++ 實現(xiàn),主要負(fù)責(zé)加載 java 基礎(chǔ)類(譬如<JAVA_HOME>/lib/rt.jar,常用的 String、List 等都位于此包下),啟動類加載器無法被 java 程序直接引用。

  • 擴(kuò)展類加載器(Extension ClassLoader):此加載器實現(xiàn)類為 sun.misc.Launcher$ExtClassLoader,負(fù)責(zé)加載 java 的一些擴(kuò)展類(一般為<JAVA_HOME>/lib/ext目錄下的 jar 包),開發(fā)者可直接使用。

  • 應(yīng)用程序類加載器(Application ClassLoader):此加載器實現(xiàn)類為 sun.misc.Launcher$AppClassLoader,負(fù)責(zé)加載應(yīng)用程序的類,包括自己寫的和引用的第三方類庫,即 classpath 類路徑中指定的類,開發(fā)者可直接使用,一個程序運行時會創(chuàng)建一個這個加載器,程序中用到加載器的地方如果沒有特殊指定一般都是這個加載器,所以也被稱為 System 系統(tǒng)類加載器。

這三個加載器具備父子委派關(guān)系(非繼承父子關(guān)系),在 java 中每個類都是由某個類加載器的實體來載入的,所以在 Class 類的實體中都會有字段記錄載入它的類加載器的實體(當(dāng)為 null 時,其指 Bootstrap ClassLoader),在 java 類加載器中除了引導(dǎo)類加載器(既 Bootstrap ClassLoader),所有的類加載器都有一個父類加載器(因為他們本身自己就是 java 類),子 ClassLoader 有一個變量 parent 指向父 ClassLoader,在子 ClassLoader 加載類時一般會先通過父 ClassLoader 加載,所以在加載一個 class 文件時首先會判斷是否已經(jīng)加載過了,加載過則直接返回 Class 對象(一個類只會被一個 ClassLoader 加載一次),沒加載過則先讓父 ClassLoader 去加載,如果加載成功則返回得到的 Class 對象;父 ClassLoader 沒有加載成功則嘗試自己加載,自己加載不成功則拋出 ClassNotFoundException,整個這個加載流程就是雙親委派模型,即優(yōu)先讓父 ClassLoader 加載;雙親委派可以從優(yōu)先級的策略上避免 Java 類庫被覆蓋的問題,例如類 java.long.Object 存放在 rt.jar 中,無論哪個類加載器要加載這個類最終都會委派給啟動類加載器進(jìn)行加載,因此 Object 類在程序的各種類加載器環(huán)境中都是同一個類,相反如果我們自己寫了一個類名為 java.long.Object 且放在了程序的 classpath 中,那系統(tǒng)中將會出現(xiàn)多個不同的 Object 類,java 類型體系中最基礎(chǔ)的行為也無法保證,所以一般遵循雙親委派的加載器就不會存在這個問題。

類加載機(jī)制中的雙親委派模型只是一般情況下的機(jī)制,有些時候我們可以自定義加載順序(不建議)就不用遵守雙親委派模型了,同時以 java 開頭的類也不能被自定義類加載器加載,這是 java 安全機(jī)制保證的;ClassLoader 一般是系統(tǒng)提供的,不需要自己實現(xiàn),不過通過自定義 ClassLoader 可以實現(xiàn)一些靈活強大的功能,譬如熱部署(不重啟 Java 程序的情況下動態(tài)替換類實現(xiàn))、應(yīng)用的模塊化和隔離化(不同 ClassLoader 可以加載相同的類,但是互相隔離互不影響,tomcat 就是利用這個特性管理多 web 應(yīng)用的)、靈活加載等,通過自定義類加載器我們可以加載其它位置的類或 jar,自定義類加載器主要步驟為繼承 java.lang.ClassLoader 然后重寫父類的 findClass 方法,之所以一般只重寫這一個方法是因為 JDK 已經(jīng)在 loadClass 方法中幫我們實現(xiàn)了 ClassLoader 搜索類的算法,當(dāng)在 loadClass 方法中搜索不到類時 loadClass 方法會主動調(diào)用 findClass 方法來搜索類,所以我們只需重寫該方法即可,如沒有特殊的要求,一般不建議重寫 loadClass 搜索類的算法。

JVM 在判定兩個 Class 是否相同時不僅會判斷兩個類名是否相同而且會判斷是否由同一個類加載器實例加載的,只有兩者同時滿足的情況下, JVM 才認(rèn)為這兩個 Class 是相同的,就算兩個 Class 是同一份 class 字節(jié)碼文件,如果被兩個不同的 ClassLoader 實例所加載 JVM 也會認(rèn)為它們是兩個不同 Class,比如字節(jié)碼文件 Simple.class 被 ClassLoaderA 和 ClassLoaderB 這兩個類加載器分別加載并分別得到了 Class 實例,而對于 JVM 來說它們是兩個不同的實例對象,但它們確實是同一份字節(jié)碼文件,當(dāng)試圖將這個 Class 實例生成具體的對象進(jìn)行轉(zhuǎn)換時就會拋運行時異常 java.lang.ClassCaseException 提示這是兩個不同的類型;此外一個 ClassLoader 創(chuàng)建時如果沒有指定 parent, 則 parent 默認(rèn)就是 AppClassLoader(應(yīng)用程序類加載器)。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容