加载类_JVM类加载详解
類的加載器
概述
類加載器是JVM執(zhí)行類加載機(jī)制的前提。
ClassLoader的作用:ClassLoader是Java的核心組件,所有的Class都是由ClassLoader進(jìn)行加載的,ClassLoader負(fù)責(zé)通過各種方式將Class信息的二進(jìn)制數(shù)據(jù)流讀入JVM內(nèi)部,轉(zhuǎn)換為一個(gè)與目標(biāo)類對應(yīng)的java.lang.Class對象實(shí)例。然后交給Java虛擬機(jī)進(jìn)行鏈接、初始化等操作。因此,ClassLoader在整個(gè)裝載階段,只能影響到類的加載,而無法通過ClassLoader去改變類的鏈接和初始化行為。至于它是否可以運(yùn)行,則由Execution Engine決定。
jvm類加載分類
類的加載分類:顯式加載 vs 隱式加載
class文件的顯式加載與隱式加載的方式是指JVM加載class文件到內(nèi)存的方式。
- 顯式加載指的是在代碼中通過調(diào)用ClassLoader加載class對象,如直接使用Class.forName(name)或this.getClass().getClassLoader().loadClass()加載class對象。
- 隱式加載則是不直接在代碼中調(diào)用ClassLoader的方法加載class對象,而是通過虛擬機(jī)自動(dòng)加載到內(nèi)存中,如在加載某個(gè)類的class文件時(shí),該類的class文件中引用了另外一個(gè)類的對象,此時(shí)額外引用的類將通過JVM自動(dòng)加載到內(nèi)存中。
代碼示例:
User?user=new?User();//隱式加載Class?clazz=Class.forName("com.atguigu.java.User");//顯式加載并初始化
ClassLoader.getSystemClassLoader().loadClass("T1.Parent");?//顯式加載,但不初始化
類加載器的必要性
一般情況下,Java開發(fā)人員并不需要在程序中顯式地使用類加載器,但是了解類加載器的加載機(jī)制卻顯得至關(guān)重要。從以下幾個(gè)方面說:
- 避免在開發(fā)中遇到j(luò)ava.lang.ClassNotFoundException異常或java.lang.NoClassDefFoundError異常時(shí),手足無措。只有了解類加載器的 加載機(jī)制才能夠在出現(xiàn)異常的時(shí)候快速地根據(jù)錯(cuò)誤異常日志定位問題和解決問題
- 需要支持類的動(dòng)態(tài)加載或需要對編譯后的字節(jié)碼文件進(jìn)行加解密操作時(shí),就需要與類加載器打交道了。
- 開發(fā)人員可以在程序中編寫自定義類加載器來重新定義類的加載規(guī)則,以便實(shí)現(xiàn)一些自定義的處理邏輯。
命名空間
什么是類的唯一性?
對于任意一個(gè)類,都需要由加載它的類加載器和這個(gè)類本身一同確認(rèn)其在Java虛擬機(jī)中的唯一性。每一個(gè)類加載器,都擁有一個(gè)獨(dú)立的類名稱空間:比較兩個(gè)類是否相等,只有在這兩個(gè)類是由同一個(gè)類加載器加載的前提下才有意義。否則,即使這兩個(gè)類源自同一個(gè)Class文件,被同一個(gè)虛擬機(jī)加載,只要加載他們的類加載器不同,那這兩個(gè)類就必定不相等。
命名空間
- 每個(gè)類加載器都有自己的命名空間,命名空間由該加載器及所有的父加載器所加載的類組成
- 在同一命名空間中,不會(huì)出現(xiàn)類的完整名字(包括類的包名)相同的兩個(gè)類
- 在不同的命名空間中,有可能會(huì)出現(xiàn)類的完整名字(包括類的包名)相同的兩個(gè)類
類加載機(jī)制的基本特征
- 雙親委派模型。但不是所有類加載都遵守這個(gè)模型,有的時(shí)候,啟動(dòng)類加載器所加載的類型,是可能要加載用戶代碼的,比如JDK內(nèi)部的ServiceProvider/ServiceLoader機(jī)制,用戶可以在標(biāo)準(zhǔn)API框架上,提供自己的實(shí)現(xiàn),JDK也需要提供些默認(rèn)的參考實(shí)現(xiàn)。例如,Java中JNDI、JDBC、文件系統(tǒng)、Cipher等很多方面,都是利用的這種機(jī)制,這種情況就不會(huì)用雙親委派模型去加載,而是利用所謂的上下文加載器。
- 可見性,子類加載器可以訪問父加載器加載的類型,但是反過來是不允許的。不然,因?yàn)槿鄙俦匾母綦x,我們就沒有辦法利用類加載器去實(shí)現(xiàn)容器的邏輯。
- 單一性,由于父加載器的類型對于子加載器是可見的,所以父加載器中加載過的類型,就不會(huì)在子加載器中重復(fù)加載。但是注意,類加載器“鄰居”間,同一類型仍然可以被加載多次,因?yàn)榛ハ嗖⒉豢梢姟?/li>
類的加載器分類
JVM支持兩種類型的類加載器,分別為引導(dǎo)類加載器(Bootstrap ClassLoader)和自定義類加載器(User-Defined ClassLoader)。
從概念上來講,自定義類加載器一般指的是程序中由開發(fā)人員自定義的一類類加載器,但是Java虛擬機(jī)規(guī)范卻沒有這么定義,而是將所有派生于抽象類ClassLoader的類加載器都劃分為自定義類加載器。無論類加載器的類型如何劃分,在程序中我們最常見的類加載器結(jié)構(gòu)主要是如下情況:
類加載器除了頂層的啟動(dòng)類加載器外,其余的類加載器都應(yīng)當(dāng)有自己的“父類”加載器。
不同類加載器看似是繼承(Inheritance)關(guān)系,實(shí)際上是包含關(guān)系。在下層加載器中,包含著上層加載器的引用。
引導(dǎo)類加載器(Bootstrap ClassLoader)
- 這個(gè)類加載使用C/C++語言實(shí)現(xiàn)的,嵌套在JVM內(nèi)部。
- 它用來加載Java的核心庫(JAVAHOME/jre/lib/rt.jar或sun.boot.class.path路徑下的內(nèi)容)。用于提供JVM自身需要的類。
- 并不繼承自java.lang.ClassLoader,沒有父加載器。
- 出于安全考慮,Bootstrap啟動(dòng)類加載器只加載包名為java、javax、sun等開頭的類
- 加載擴(kuò)展類和應(yīng)用程序類加載器,并指定為他們的父類加載器。
擴(kuò)展類加載器(Extension ClassLoader)
- Java語言編寫,由sun.misc.Launcher$ExtClassLoader實(shí)現(xiàn)。
- 繼承于ClassLoader類
- 父類加載器為啟動(dòng)類加載器
- 從java.ext.dirs系統(tǒng)屬性所指定的目錄中加載類庫,或從JDK的安裝目錄的jre/lib/ext子目錄下加載類庫。如果用戶創(chuàng)建的JAR放在此目錄下,也會(huì)自動(dòng)由擴(kuò)展類加載器加載。
代碼示例:
public?static?void?main(String[]?args)?{????//獲取BootstrapcLassLoader能夠加載的api的路徑
????URL[]?urLs?=?sun.misc.Launcher.getBootstrapClassPath().getURLs();
????for?(URL?element?:?urLs)?{
????????System.out.println(element.toExternalForm());
????}
????//????????file:/D:/java/jre/lib/resources.jar
????//????????file:/D:/java/jre/lib/rt.jar
????//????????file:/D:/java/jre/lib/sunrsasign.jar
????//????????file:/D:/java/jre/lib/jsse.jar
????//????????file:/D:/java/jre/lib/jce.jar
????//????????file:/D:/java/jre/lib/charsets.jar
????//????????file:/D:/java/jre/lib/jfr.jar
????//????????file:/D:/java/jre/classes
????//引導(dǎo)類加載
????ClassLoader?classloader?=?java.security.Provider.class.getClassLoader();
????System.out.println(classloader);?//?null
????System.out.println("***********擴(kuò)展類加載器*************");
????String?extDirs?=?System.getProperty("java.ext.dirs");
????for?(String?path?:?extDirs.split(";"))?{
????????System.out.println(path);
????}
????//????????D:\Java\jre\lib\ext
????//????????C:\Windows\Sun\Java\lib\ext
????//擴(kuò)展類加載器
????ClassLoader?classLoader1?=?sun.security.ec.CurveDB.class.getClassLoader();
????System.out.println(classLoader1);//?sun.misc.Launcher$ExtClassLoader@6e0be858
}
系統(tǒng)類加載器(AppClassLoader)
- java語言編寫,由sun.misc.Launcher$AppClassLoader實(shí)現(xiàn)
- 繼承于ClassLoader類
- 父類加載器為擴(kuò)展類加載器
- 它負(fù)責(zé)加載環(huán)境變量classpath或系統(tǒng)屬性java.class.path 指定路徑下的類庫
- 應(yīng)用程序中的類加載器默認(rèn)是系統(tǒng)類加載器。
- 它是用戶自定義類加載器的默認(rèn)父加載器
- 通過ClassLoader的getSystemClassLoader()方法可以獲取到該類加載器
用戶自定義類加載器
- 在Java的日常應(yīng)用程序開發(fā)中,類的加載幾乎是由上述3種類加載器相互配合執(zhí)行的。在必要時(shí),我們還可以自定義類加載器,來定制類的加載方式。
- 體現(xiàn)Java語言強(qiáng)大生命力和巨大魅力的關(guān)鍵因素之一便是,Java開發(fā)者可以自定義類加載器來實(shí)現(xiàn)類庫的動(dòng)態(tài)加載,加載源可以是本地的JAR包,也可以是網(wǎng)絡(luò)上的遠(yuǎn)程資源。
- 通過類加載器可以實(shí)現(xiàn)非常絕妙的插件機(jī)制,這方面的實(shí)際應(yīng)用案例舉不勝舉。例如,著名的OSGI組件框架,再如Eclipse的插件機(jī)制。類加載器為應(yīng)用程序提供了一種動(dòng)態(tài)增加新功能的機(jī)制,這種機(jī)制無須重新打包發(fā)布應(yīng)用程序就能實(shí)現(xiàn)。
- 同時(shí),自定義加載器能夠?qū)崿F(xiàn)應(yīng)用隔離,例如Tomcat,Spring等中間件和組件框架都在內(nèi)部實(shí)現(xiàn)了自定義的加載器,并通過自定義加載器隔離不同的組件模塊。這種機(jī)制比C/C++程序要好太多,想不修改C/C++程序就能為其新增功能,幾乎是不可能的,僅僅一個(gè)兼容性便能阻擋住所有美好的設(shè)想。
- 自定義類加載器通常需要繼承ClassLoader。
ClassLoader源碼分析
ClassLoader與現(xiàn)有類加載器的關(guān)系:
ClassLoader的主要方法
public final ClassLoader getParent() :返回該類加載器的超類加載器
**public Class> loadClass(String name) **:加載名稱為name的類,返回結(jié)果為java.lang.Class類的實(shí)例。如果找不到類,則返回ClassNotFoundException異常。該方法中的邏輯就是使用雙親委派機(jī)制實(shí)現(xiàn)的。
protected?Class>?loadClass(String?name,?boolean?resolve)?//resolve?=?false?不進(jìn)行解析
????????throws?ClassNotFoundException
????{
????????synchronized?(getClassLoadingLock(name))?{
????????????//?First,?check?if?the?class?has?already?been?loaded
????????????//?查看該類是否被加載過
????????????Class>?c?=?findLoadedClass(name);
????????????if?(c?==?null)?{
????????????????long?t0?=?System.nanoTime();
????????????????try?{
????????????????????//獲取當(dāng)前類父類的加載器
????????????????????if?(parent?!=?null)?{
????????????????????????//使用父類的加載器加載
????????????????????????c?=?parent.loadClass(name,?false);
????????????????????}?else?{
????????????????????????//說明父類加載器為引導(dǎo)類加載器
????????????????????????c?=?findBootstrapClassOrNull(name);
????????????????????}
????????????????}?catch?(ClassNotFoundException?e)?{
????????????????????//?ClassNotFoundException?thrown?if?class?not?found
????????????????????//?from?the?non-null?parent?class?loader
????????????????}
????????????????if?(c?==?null)?{//當(dāng)前類的加載器父類加載器未加載此類?or?當(dāng)前類的加載器未加載此類
????????????????????//?If?still?not?found,?then?invoke?findClass?in?order
????????????????????//?to?find?the?class.
????????????????????long?t1?=?System.nanoTime();
????????????????????//調(diào)用當(dāng)前Classloader的findClass
????????????????????c?=?findClass(name);
????????????????????//?this?is?the?defining?class?loader;?record?the?stats
????????????????????sun.misc.PerfCounter.getParentDelegationTime().addTime(t1?-?t0);
????????????????????sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
????????????????????sun.misc.PerfCounter.getFindClasses().increment();
????????????????}
????????????}
????????????if?(resolve)?{?//是否解析
????????????????resolveClass(c);
????????????}
????????????return?c;
????????}
}**protected Class > findClass(String name) **:查找二進(jìn)制名稱為name的類,返回結(jié)果為java.lang.Class類的實(shí)例。這是一個(gè)受保護(hù)的方法,JVM鼓勵(lì)我們重寫此方法,需要自定義加載器遵循雙親委托機(jī)制,該方法會(huì)在檢查完父類加載器之后被loadClass()方法調(diào)用。
protected?Class>?findClass(String?name)?throws?ClassNotFoundException?{
????????throw?new?ClassNotFoundException(name);
}我們找到對應(yīng)重寫該方法的地方。URLClassLoader類。
protected?Class>?findClass(final?String?name)
????????throws?ClassNotFoundException
????{
????????final?Class>?result;
????????try?{
????????????result?=?AccessController.doPrivileged(
????????????????new?PrivilegedExceptionAction>()?{public?Class>?run()?throws?ClassNotFoundException?{//全路徑替換
????????????????????????String?path?=?name.replace('.',?'/').concat(".class");
????????????????????????Resource?res?=?ucp.getResource(path,?false);if?(res?!=?null)?{try?{//調(diào)用defineClass生成Class對象return?defineClass(name,?res);
????????????????????????????}?catch?(IOException?e)?{throw?new?ClassNotFoundException(name,?e);
????????????????????????????}
????????????????????????}?else?{return?null;
????????????????????????}
????????????????????}
????????????????},?acc);
????????}?catch?(java.security.PrivilegedActionException?pae)?{throw?(ClassNotFoundException)?pae.getException();
????????}if?(result?==?null)?{throw?new?ClassNotFoundException(name);
????????}return?result;
}protected final Class>defineclass(String name,byte[]b,int off,int len):根據(jù)給定的字節(jié)數(shù)組b轉(zhuǎn)換為Class的實(shí)例,off和len參數(shù)表示實(shí)際Class信息在byte數(shù)組中的位置和長度,其中byte數(shù)組b是ClassLoader從外部獲取的。這是受保護(hù)的方法,只有在自定義ClassLoader子類中可以使用。
defineClass()方法是用來將byte字節(jié)流解析成JVM能夠識別的Class對象(ClassLoader中已實(shí)現(xiàn)該方法邏輯),通過這個(gè)方法不僅能夠通過class文件實(shí)例化class對象,也可以通過其他方式實(shí)例化Class對象,如通過網(wǎng)絡(luò)接收一個(gè)類的字節(jié)碼,然后轉(zhuǎn)換為byte字節(jié)流創(chuàng)建對應(yīng)的Class對象。
defineClass()方法通常與findClass()方法一起使用,一般情況下,在自定義類加載器時(shí),會(huì)直接覆蓋ClassLoader的findClass()方法并編寫加載規(guī)則,取得要加載類的字節(jié)碼后轉(zhuǎn)換成流,然后調(diào)用defineClass()方法生成類的Class對象
代碼示例:
@Override
protected?Class>findClass(String?name)throws?ClassNotFoundException{
????//獲取類的字節(jié)數(shù)組
????byte[]classData=getClassData(name);
????if(classData==null){
????????throw?new?ClassNotFoundException();
????}else{
????????//使用defineclass生成class對象
????????return?defineclass(name,classData,0,classData.length);
????}
}protected final void resolveClass(Class>c):鏈接指定的一個(gè)Java類。使用該方法可以使用類的Class對象創(chuàng)建完成的同時(shí)也被解析。前面我們說鏈接階段主要是對字節(jié)碼進(jìn)行驗(yàn)證,為類變量分配內(nèi)存并設(shè)置初始值同時(shí)將字節(jié)碼文件中的符號引用轉(zhuǎn)換為直接引用。
protected final Class>findLoadedClass(String name):查找名稱為name的已經(jīng)被加載過的類,返回結(jié)果為java.lang.Class類的實(shí)例。這個(gè)方法是final方法,無法被修改。
**private final ClassLoader parent:**它也是一個(gè)ClassLoader的實(shí)例,這個(gè)字段所表示的ClassLoader也稱為這個(gè)ClassLoader的雙親。在類加載的過程中,ClassLoader可能會(huì)將某些請求交予自己的雙親處理。
SecureClassLoader 與 URLClassLoader
SecureClassLoader擴(kuò)展了ClassLoader,新增了幾個(gè)與使用相關(guān)的代碼源(對代碼源的位置及其證書的驗(yàn)證)和權(quán)限定義類驗(yàn)證(主要指對class源碼的訪問權(quán)限)的方法,一般我們不會(huì)直接跟這個(gè)類打交道,更多是與它的子類URLClassLoader有所關(guān)聯(lián)。
前面說過,ClassLoader是一個(gè)抽象類,很多方法是空的沒有實(shí)現(xiàn),比如findClass()、findResource()等。而URLClassLoader這個(gè)實(shí)現(xiàn)類為這些方法提供了具體的實(shí)現(xiàn)。并新增了URLClassPath類協(xié)助取得Class字節(jié)碼流等功能。在編寫自定義類加載器時(shí),如果沒有太過于復(fù)雜的需求,可以直接繼承URLClassLoader類,這樣就可以避免自己去編寫findClass()方法及其獲取字節(jié)碼流的方式,使自定義類加載器編寫更加簡潔。
雙親委派模型
如果一個(gè)類加載器在接到加載類的請求時(shí),它首先不會(huì)自己嘗試去加載這個(gè)類,而是把這個(gè)請求任務(wù)委托給父類加載器去完成,依次遞歸,如果父類加載器可以完成類加載任務(wù),就成功返回。只有父類加載器無法完成此加載任務(wù)時(shí),才自己去加載。
本質(zhì)
規(guī)定了類加載的順序是:引導(dǎo)類加載器先加載,若加載不到,由擴(kuò)展類加載器加載,若還加載不到,才會(huì)由系統(tǒng)類加載器或自定義的類加載器進(jìn)行加載。
雙親委派機(jī)制類加載流程優(yōu)勢與劣勢
優(yōu)勢
- 避免類的重復(fù)加載,確保一個(gè)類的全局唯一性
- Java類隨著它的類加載器一起具備了一種帶有優(yōu)先級的層次關(guān)系,通過這種層級關(guān)可以避免類的重復(fù)加載,當(dāng)父親已經(jīng)加載了該類時(shí),就沒有必要子ClassLoader再加載一次。
- 保護(hù)程序安全,防止核心API被隨意篡改
問題
如果在自定義的類加載器中重寫java.lang.ClassLoader.loadClass(String)或java.lang.ClassLoader.loadclass(String,boolean)方法,抹去其中的雙親委派機(jī)制,那么是不是就能夠加載核心類庫了呢?這也不行!因?yàn)镴DK還為核心類庫提供了一層保護(hù)機(jī)制。不管是自定義的類加載器,還是系統(tǒng)類加載器抑或擴(kuò)展類加載器,最終都必須調(diào)用 java.lang.ClassLoader.defineclass(String,byte[],int,int,ProtectionDomain)方法,而該方法會(huì)執(zhí)行preDefineClass()接口,該接口中提供了對JDK核心類庫的保護(hù)。
劣勢
- 檢查類是否加載的委托過程是單向的,這個(gè)方式雖然從結(jié)構(gòu)上說比較清晰,使各個(gè)ClassLoader的職責(zé)非常明確,但是同時(shí)會(huì)帶來一個(gè)問題,即頂層的ClassLoader無法訪問底層的ClassLoader所加載的類。
- 通常情況下,啟動(dòng)類加載器中的類為系統(tǒng)核心類,包括一些重要的系統(tǒng)接口,而在應(yīng)用類加載器中,為應(yīng)用類。按照這種模式,應(yīng)用類訪問系統(tǒng)類自然是沒有問題,但是系統(tǒng)類訪問應(yīng)用類就會(huì)出現(xiàn)問題。比如在系統(tǒng)類中提供了一個(gè)接口,該接口需要在應(yīng)用類中得以實(shí)現(xiàn),該接口還綁定一個(gè)工廠方法,用于創(chuàng)建該接口的實(shí)例,而接口和工廠方法都在啟動(dòng)類加載器中。這時(shí),就會(huì)出現(xiàn)該工廠方法無法創(chuàng)建由應(yīng)用類加載器加載的應(yīng)用實(shí)例的問題。
自定義類加載器
為什么要自定義類加載器?
- 隔離加載類
- 在某些框架內(nèi)進(jìn)行中間件與應(yīng)用的模塊隔離,把類加載到不同的環(huán)境。比如:阿里內(nèi)某容器框架通過自定義類加載器確保應(yīng)用中依賴的jar包不會(huì)影響到中間件運(yùn)行時(shí)使用的jar包。再比如:Tomcat這類Web應(yīng)用服務(wù)器,內(nèi)部自定義了好幾種類加載器,用于隔離同一個(gè)Web應(yīng)用服務(wù)器上的不同應(yīng)用程序。
- 修改類加載的方式
- 類的加載模型并非強(qiáng)制,除Bootstrap外,其他的加載并非一定要引入,或者根據(jù)實(shí)際情況在某個(gè)時(shí)間點(diǎn)進(jìn)行按需進(jìn)行動(dòng)態(tài)加載
- 擴(kuò)展加載源
- 比如從數(shù)據(jù)庫、網(wǎng)絡(luò)、甚至是電視機(jī)機(jī)頂盒進(jìn)行加載
- 防止源碼泄漏
- Java代碼容易被編譯和篡改,可以進(jìn)行編譯加密。那么類加載也需要自定義,還原加密的字節(jié)碼。
常見的場景
- 實(shí)現(xiàn)類似進(jìn)程內(nèi)隔離,類加載器實(shí)際上用作不同的命名空間,以提供類似容器、模塊化的效果。例如,兩個(gè)模塊依賴于某個(gè)類庫的不同版本,如果分別被不同的容器加載,就可以互不干擾。這個(gè)方面的集大成者是JavaEE和OSGI、JPMS等框架。
- 應(yīng)用需要從不同的數(shù)據(jù)源獲取類定義信息,例如網(wǎng)絡(luò)數(shù)據(jù)源,而不是本地文件系統(tǒng)。或者是需要自己操縱字節(jié)碼,動(dòng)態(tài)修改或者生成類型。
注意
在一般情況下,使用不同的類加載器去加載不同的功能模塊,會(huì)提高應(yīng)用程序的安全性。但是,如果涉及Java類型轉(zhuǎn)換,則加載器反而容易產(chǎn)生不美好的事情。在做Java類型轉(zhuǎn)換時(shí),只有兩個(gè)類型都是由同一個(gè)加載器所加載,才能進(jìn)行類型轉(zhuǎn)換,否則轉(zhuǎn)換時(shí)會(huì)發(fā)生異常。
實(shí)現(xiàn)方式
Java提供了抽象類java.lang.ClassLoader,所有用戶自定義的類加載器都應(yīng)該繼承ClassLoader類。
在自定義ClassLoader的子類時(shí)候,我們常見的會(huì)有兩種做法:
- 方式一:重寫loadClass()方法
- 方式二:重寫findclass()方法(推薦)
這兩種方法本質(zhì)上差不多,畢竟loadClass()也會(huì)調(diào)用findClass(),但是從邏輯上講我們最好不要直接修改loadClass()的內(nèi)部邏輯。建議的做法是只在findClass()里重寫自定義類的加載方法,根據(jù)參數(shù)指定類的名字,返回對應(yīng)的Class對象的引用。
loadclass()這個(gè)方法是實(shí)現(xiàn)雙親委派模型邏輯的地方,擅自修改這個(gè)方法會(huì)導(dǎo)致模型被破壞,容易造成問題。因此我們最好是在雙親委派模型框架內(nèi)進(jìn)行小范圍的改動(dòng),不破壞原有的穩(wěn)定結(jié)構(gòu)。同時(shí),也避免了自己重寫loadClass()方法的過程中必須寫雙親委托的重復(fù)代碼,從代碼的復(fù)用性來看,不直接修改這個(gè)方法始終是比較好的選擇。
當(dāng)編寫好自定義類加載器后,便可以在程序中調(diào)用loadClass()方法來實(shí)現(xiàn)類加載操作。
代碼示例:
public?class?MyClassLoad?extends?ClassLoader?{????private?String?byteCodePath;
????public?MyClassLoad(String?byteCodePath)?{
????????this.byteCodePath?=?byteCodePath;
????}
????@Override
????protected?Class>?findClass(String?name)?throws?ClassNotFoundException?{
????????BufferedInputStream?bufferedInputStream?=?null;
????????ByteArrayOutputStream?byteOutputStream?=?null;
????????try?{
????????????//?獲取完整的字節(jié)碼文件路徑+class文件名
????????????String?fileName?=?byteCodePath?+?name?+?".class";
????????????//?獲取一個(gè)輸入流
????????????bufferedInputStream?=?new?BufferedInputStream(new?FileInputStream(fileName));
????????????//?獲取一個(gè)輸出流
????????????byteOutputStream?=?new?ByteArrayOutputStream();
????????????//?具體讀取數(shù)據(jù)并寫出的過程
????????????int?len;
????????????byte[]?data?=?new?byte[1024];
????????????while?((len?=?bufferedInputStream.read(data))?!=?-1)?{
????????????????byteOutputStream.write(data,?0,?len);
????????????}
????????????//?將輸出流轉(zhuǎn)成數(shù)組
????????????byte[]?byteCode?=?byteOutputStream.toByteArray();
????????????//?調(diào)用defineClass(),將字節(jié)數(shù)組轉(zhuǎn)成Class實(shí)列
????????????Class>?aClass?=?defineClass(null,?byteCode,?0,?byteCode.length);
????????????return?aClass;
????????}?catch?(IOException?e)?{
????????????e.printStackTrace();
????????}?finally?{
????????????try?{
????????????????if?(bufferedInputStream?!=?null)?{
????????????????????bufferedInputStream.close();
????????????????}
????????????}?catch?(IOException?e)?{
????????????????e.printStackTrace();
????????????}
????????????try?{
????????????????if?(byteOutputStream?!=?null)?{
????????????????????byteOutputStream.close();
????????????????}
????????????}?catch?(IOException?e)?{
????????????????e.printStackTrace();
????????????}
????????}
????????return?null;
????}
}
測試類:
public?class?MyClassLoadTest?{????public?static?void?main(String[]?args)?throws?ClassNotFoundException?{
????????MyClassLoad?myClassLoad?=?new?MyClassLoad("D:/");
????????Class>?myClassLoadClass?=?myClassLoad.findClass("Demo1");
????????System.out.println(myClassLoadClass.getClassLoader().getClass().getName());??System.out.println(myClassLoadClass.getClassLoader().getParent().getClass().getName());?
????}
}
Java9新特性
為了保證兼容性,JDK9沒有從根本上改變?nèi)龑宇惣虞d器架構(gòu)和雙親委派模型,但為了模塊化系統(tǒng)的順利運(yùn)行,仍然發(fā)生了一些值得被注意的變動(dòng)。
變化
擴(kuò)展機(jī)制被移除,擴(kuò)展類加載器由于向后兼容性的原因被保留,不過被重命名為平臺類加載器(platform class loader)。可以通過classLoader的新方法getPlatformClassLoader()來獲取。
JDK9時(shí)基于模塊化進(jìn)行構(gòu)建(原來的rt.jar和tools.jar被拆分成數(shù)十個(gè)JMOD文件),其中的Java類庫就已天然地滿足了可擴(kuò)展的需求,那自然無須再保留\lib\ext目錄,此前使用這個(gè)目錄或者java.ext.dirs系統(tǒng)變量來擴(kuò)展JDK功能的機(jī)制已經(jīng)沒有繼續(xù)存在的價(jià)值了。
平臺類加載器和應(yīng)用程序類加載器都不再繼承自java.net.URLClassLoader。
現(xiàn)在啟動(dòng)類加載器、平臺類加載器、應(yīng)用程序類加載器全都繼承于jdk.internal.loader.BuiltinClassLoader。
總結(jié)
以上是生活随笔為你收集整理的加载类_JVM类加载详解的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 交易猫如何卖号
- 下一篇: log4j 禁止类输出日志_log4j