JavaSE知識(shí)點(diǎn)17java類加載器詳解

1 什么是ClassLoader?

  • 1 大家都知道拨脉,當(dāng)我們寫好一個(gè)Java程序之后赁温,不是管是CS還是BS應(yīng)用界牡,都是由若干個(gè).class文件組織而成的一個(gè)完整的Java應(yīng)用程序冈欢,當(dāng)程序在運(yùn)行時(shí),即會(huì)調(diào)用該程序的一個(gè)入口函數(shù)來調(diào)用系統(tǒng)的相關(guān)功能谬晕,而這些功能都被封裝在不同的class文件當(dāng)中碘裕,所以經(jīng)常要從這個(gè)class文件中要調(diào)用另外一個(gè)class文件中的方法,如果另外一個(gè)文件不存在攒钳,則會(huì)引發(fā)系統(tǒng)異常帮孔。
  • 2 而程序在啟動(dòng)的時(shí)候,并不會(huì)一次性加載程序所要用的所有class文件不撑,而是根據(jù)程序的需要文兢,通過Java的類加載機(jī)制(ClassLoader)來動(dòng)態(tài)加載某個(gè)class文件到內(nèi)存當(dāng)中的,從而只有class文件被載入到了內(nèi)存之后焕檬,才能被其它c(diǎn)lass所引用姆坚。所以ClassLoader就是用來動(dòng)態(tài)加載class文件到內(nèi)存當(dāng)中用的。

2 Java提供的三個(gè)默認(rèn)ClassLoader

  • 1 BootStrap ClassLoader:稱為啟動(dòng)類加載器实愚,是Java類加載層次中最頂層的類加載器兼呵,負(fù)責(zé)加載JDK中的核心類庫,如:rt.jar腊敲、resources.jar击喂、charsets.jar等,可通過查找sun.boot.class.path這個(gè)系統(tǒng)屬性碰辅,得知該類加載器從哪些地方加載了相關(guān)的jar或class文件懂昂。
System.out.println(System.getProperty("sun.boot.class.path"));  

輸出結(jié)果為:

C:\Program Files\Java\jdk1.6.0_22\jre\lib\resources.jar;
C:\Program Files\Java\jdk1.6.0_22\jre\lib\rt.jar;
C:\Program Files\Java\jdk1.6.0_22\jre\lib\sunrsasign.jar;
C:\Program Files\Java\jdk1.6.0_22\jre\lib\jsse.jar;
C:\Program Files\Java\jdk1.6.0_22\jre\lib\jce.jar;
C:\Program Files\Java\jdk1.6.0_22\jre\lib\charsets.jar;
C:\Program Files\Java\jdk1.6.0_22\jre\classes
  • 2 Extension ClassLoader:稱為擴(kuò)展類加載器,負(fù)責(zé)加載Java的擴(kuò)展類庫没宾,默認(rèn)加載JAVA_HOME/jre/lib/ext/目下的所有jar忍法。
  • 3 App ClassLoader:稱為系統(tǒng)類加載器,負(fù)責(zé)加載應(yīng)用程序classpath目錄下的所有jar和class文件榕吼。
  • 4 總結(jié)
    Extension ClassLoader和App ClassLoader也繼承自java.lang.ClassLoader類,但是Bootstrap ClassLoader不繼承自java.lang.ClassLoader類勉失,因?yàn)樗皇且粋€(gè)普通的Java類羹蚣,底層由C++編寫,已嵌入到了JVM內(nèi)核當(dāng)中乱凿,當(dāng)JVM啟動(dòng)后顽素,Bootstrap ClassLoader也隨著啟動(dòng)咽弦,負(fù)責(zé)加載完核心類庫后,并構(gòu)造Extension ClassLoader和App ClassLoader類加載器胁出。

3 ClassLoader加載類的原理

  • 1 原理簡介
    1)ClassLoader使用的是雙親委托模型來搜索類的型型,每個(gè)ClassLoader實(shí)例都有一個(gè)父類加載器的引用(不是繼承的關(guān)系,是一個(gè)包含的關(guān)系)全蝶。
    2)類加載器的包含關(guān)系如下:
    自定義類加載器————>系統(tǒng)類加載器————>擴(kuò)展類加載器————>根加載器
    3)當(dāng)一個(gè)自定義ClassLoader實(shí)例需要加載某個(gè)類時(shí)闹蒜,先從自己的命名空間里查找是否已加載該類,如果已加載抑淫,則直接返回代表該類的Class對(duì)象的引用绷落。
    4)如果沒加載,才委托其父類加載器進(jìn)行加載始苇。其父類加載器命名空間沒有在砌烁,則一層層向上委托,直到根加載器催式。然后由根加載器開始從上到下加載該類函喉。一旦加載到則將這個(gè)找到的類生成一個(gè)類的定義,并將它加載到內(nèi)存當(dāng)中荣月,最后返回這個(gè)類在內(nèi)存中的Class實(shí)例對(duì)象的引用管呵。
    5)如果所有加載器都沒有加載到這個(gè)類時(shí),則拋出ClassNotFoundException異常喉童。
  • 2 為什么要使用雙親委托這種模型呢撇寞?
    1)這樣可以避免重復(fù)加載,當(dāng)父親已經(jīng)加載了該類的時(shí)候堂氯,就沒有必要子ClassLoader再加載一次蔑担。
    2)考慮到安全因素,我們試想一下咽白,如果不使用這種委托模式啤握,那我們就可以隨時(shí)使用自定義的String來動(dòng)態(tài)替代java核心api中定義的類型,這樣會(huì)存在非常大的安全隱患晶框,而雙親委托的方式排抬,就可以避免這種情況,因?yàn)镾tring已經(jīng)在啟動(dòng)時(shí)就被引導(dǎo)類加載器(Bootstrcp ClassLoader)加載授段,所以用戶自定義的ClassLoader永遠(yuǎn)也無法加載一個(gè)自己寫的String蹲蒲,除非你改變JDK中ClassLoader搜索類的默認(rèn)算法。
  • 3 JVM在搜索類的時(shí)候侵贵,又是如何判定兩個(gè)class是相同的呢届搁?
    1)JVM在判定兩個(gè)class是否相同時(shí),不僅要判斷兩個(gè)類名是否相同,而且要判斷是否由同一個(gè)類加載器實(shí)例加載的卡睦。只有兩者同時(shí)滿足的情況下宴胧,JVM才認(rèn)為這兩個(gè)class是相同的。就算兩個(gè)class是同一份class字節(jié)碼表锻,如果被兩個(gè)不同的ClassLoader實(shí)例所加載恕齐,JVM也會(huì)認(rèn)為它們是兩個(gè)不同class。
    2)比如網(wǎng)絡(luò)上的一個(gè)Java類瞬逊,javac編譯之后生成字節(jié)碼文件显歧,同一個(gè)自定義類加載器的兩個(gè)實(shí)例同時(shí)讀取了這個(gè)字節(jié)碼文件,并分別定義出了java.lang.Class實(shí)例來表示這個(gè)類码耐,對(duì)于JVM來說追迟,它們是兩個(gè)不同的實(shí)例對(duì)象,但它們確實(shí)是同一份字節(jié)碼文件骚腥,如果試圖將這個(gè)Class實(shí)例生成具體的對(duì)象進(jìn)行轉(zhuǎn)換時(shí)敦间,就會(huì)拋運(yùn)行時(shí)異常java.lang.ClassCaseException,提示這是兩個(gè)不同的類型束铭。

4 定義自已的ClassLoader

  • 1 既然JVM已經(jīng)提供了默認(rèn)的類加載器廓块,為什么還要定義自已的類加載器呢?
    因?yàn)镴ava中提供的默認(rèn)ClassLoader契沫,只加載指定目錄下的jar和class带猴,如果我們想加載其它位置的類或jar時(shí),比如:我要加載網(wǎng)絡(luò)上的一個(gè)class文件懈万,通過動(dòng)態(tài)加載到內(nèi)存之后拴清,要調(diào)用這個(gè)類中的方法實(shí)現(xiàn)我的業(yè)務(wù)邏輯。在這樣的情況下会通,默認(rèn)的ClassLoader就不能滿足我們的需求了口予,所以需要定義自己的ClassLoader。
  • 2 定義自已的類加載器分為兩步:
    1)繼承java.lang.ClassLoader
    2)重寫父類的findClass方法
  • 3 父類有那么多方法涕侈,為什么偏偏只重寫findClass方法沪停?
    因?yàn)镴DK已經(jīng)在loadClass方法中幫我們實(shí)現(xiàn)了ClassLoader搜索類的算法,當(dāng)在loadClass方法中搜索不到類時(shí)裳涛,loadClass方法就會(huì)調(diào)用findClass方法來搜索類木张,所以我們只需重寫該方法即可。
  • 4 代碼舉例
package lwlstudy.test.classloader;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
public class MyClassLoader extends ClassLoader {

    private String classPath;

    public MyClassLoader(String classPath) {
        this.classPath = classPath;
    }

    public String getClassPath() {
        return classPath;
    }

    public void setClassPath(String classPath) {
        this.classPath = classPath;
    }

    public MyClassLoader(ClassLoader parent) {
        super(parent);
    }

    @Override
    protected Class<?> findClass(String className) throws ClassNotFoundException {
        byte[] data = this.getData(className);
        if (data == null) {
            throw new ClassNotFoundException();
        } else {
            return defineClass(className, data, 0, data.length);
        }
    }

    private byte[] getData(String className) {

        String path = classPath + File.separatorChar + className.replace('.', File.separatorChar) + ".class";
        InputStream is = null;
        ByteArrayOutputStream stream = null;
        try {
            is = new FileInputStream(new File(path));
            stream = new ByteArrayOutputStream();
            byte[] buffer = new byte[2048];
            int num = 0;
            while ((num = is.read(buffer)) != -1) {
                stream.write(buffer, 0, num);
            }
            return stream.toByteArray();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                is.close();
                stream.close();
            } catch (IOException e2) {
                e2.printStackTrace();
            }
        }

        return null;
    }

    public static void main(String[] args) throws Exception {

        MyClassLoader mcl1 = new MyClassLoader("W:\\zz");
        Class<?> clazz = mcl1.loadClass("lwlstudy.test.classloader.Dog");
        Object obj = clazz.newInstance();
        Object obj1 = clazz.newInstance();
        System.out.println(obj.getClass().equals(obj1.getClass()));

        MyClassLoader mcl2 = new MyClassLoader("W:\\zz");
        Class<?> clazz2 = mcl2.loadClass("lwlstudy.test.classloader.Dog");
        Object obj2 = clazz2.newInstance();

        System.out.println(obj.getClass().equals(obj2.getClass()));
    }
}
```
輸出結(jié)果為:
```java
true
false
```
原因參考:如何判定兩個(gè)class是相同的呢端三?
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末舷礼,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子郊闯,更是在濱河造成了極大的恐慌且轨,老刑警劉巖浮声,帶你破解...
    沈念sama閱讀 221,888評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異旋奢,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)然痊,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,677評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門至朗,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人剧浸,你說我怎么就攤上這事锹引。” “怎么了唆香?”我有些...
    開封第一講書人閱讀 168,386評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵嫌变,是天一觀的道長。 經(jīng)常有香客問我躬它,道長腾啥,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,726評(píng)論 1 297
  • 正文 為了忘掉前任冯吓,我火速辦了婚禮倘待,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘组贺。我一直安慰自己凸舵,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,729評(píng)論 6 397
  • 文/花漫 我一把揭開白布失尖。 她就那樣靜靜地躺著啊奄,像睡著了一般。 火紅的嫁衣襯著肌膚如雪掀潮。 梳的紋絲不亂的頭發(fā)上菇夸,一...
    開封第一講書人閱讀 52,337評(píng)論 1 310
  • 那天,我揣著相機(jī)與錄音胧辽,去河邊找鬼峻仇。 笑死,一個(gè)胖子當(dāng)著我的面吹牛邑商,可吹牛的內(nèi)容都是我干的摄咆。 我是一名探鬼主播,決...
    沈念sama閱讀 40,902評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼人断,長吁一口氣:“原來是場噩夢啊……” “哼吭从!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起恶迈,我...
    開封第一講書人閱讀 39,807評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤涩金,失蹤者是張志新(化名)和其女友劉穎谱醇,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體步做,經(jīng)...
    沈念sama閱讀 46,349評(píng)論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡副渴,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,439評(píng)論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了全度。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片煮剧。...
    茶點(diǎn)故事閱讀 40,567評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖将鸵,靈堂內(nèi)的尸體忽然破棺而出勉盅,到底是詐尸還是另有隱情,我是刑警寧澤顶掉,帶...
    沈念sama閱讀 36,242評(píng)論 5 350
  • 正文 年R本政府宣布草娜,位于F島的核電站,受9級(jí)特大地震影響痒筒,放射性物質(zhì)發(fā)生泄漏宰闰。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,933評(píng)論 3 334
  • 文/蒙蒙 一凸克、第九天 我趴在偏房一處隱蔽的房頂上張望议蟆。 院中可真熱鬧,春花似錦萎战、人聲如沸咐容。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,420評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽戳粒。三九已至,卻和暖如春虫啥,著一層夾襖步出監(jiān)牢的瞬間蔚约,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,531評(píng)論 1 272
  • 我被黑心中介騙來泰國打工涂籽, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留苹祟,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,995評(píng)論 3 377
  • 正文 我出身青樓评雌,卻偏偏與公主長得像树枫,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子景东,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,585評(píng)論 2 359

推薦閱讀更多精彩內(nèi)容