ClassLoader源碼解析

一:ClassLoader
根據(jù)一個(gè)指定的類的名稱莱革,找到或者生成其對(duì)應(yīng)的字節(jié)代碼,然后從這些字節(jié)代碼中定義出一個(gè)Java類讹开,即java.lang.Class類的一個(gè)實(shí)例盅视。

二:ClassLoader的Api

1:loadclass:判斷是否已加載,使用雙親委派模型旦万,請(qǐng)求父加載器闹击,都為空,使用findclass成艘。 拋出的是java.lang.ClassNotFoundException異常赏半。
2:findclass:根據(jù)名稱或位置加載.class字節(jié)碼,然后使用defineClass淆两。(注:加載類的時(shí)候需要全限定類名)
3:findLoadedClass:查找指定名稱的已經(jīng)被加載過的類断箫,返回的結(jié)果是java.lang.Class類的實(shí)例。
4:defineclass: 把字節(jié)數(shù)組中的內(nèi)容轉(zhuǎn)換成Java類琼腔,返回的結(jié)果是java.lang.Class類的實(shí)例(解析定義.class字節(jié)流,返回class對(duì)象)踱葛。拋出的是java.lang.NoClassDefFoundError異常丹莲。
使用場(chǎng)景:對(duì)class文件的加解密操作會(huì)需要使用defineClass()來將解密后的字節(jié)數(shù)組處理成class對(duì)象。
5:resolveClass:鏈接指定的 Java 類尸诽。

三:雙親委派源碼
ClassLoader#loadClass 和 ClassLoader#defineClass

/**
* 使用指定的二進(jìn)制名稱加載類甥材。這個(gè)方法的默認(rèn)實(shí)現(xiàn)是按照以下順序搜索類:
* 調(diào)用findLoadedClass(String name)檢查類是否已經(jīng)加載。
* 在父類加載器上調(diào)用loadClass()方法性含。如果父類null洲赵,則使用虛擬機(jī)內(nèi)置的類加載器(啟動(dòng)類加載器BootStrapClassLoader)。
* 調(diào)用findClass(String)方法查找該類。
* 如果使用上述步驟找到了類叠萍,且解析標(biāo)志為true芝发,則該方法將在結(jié)果類對(duì)象上調(diào)用resolveClass(class)方法。
* 鼓勵(lì)ClassLoader的子類重寫findClass(String)苛谷,而不是loadClass(String name, boolean resolve)這個(gè)方法辅鲸。
* 除非重寫,否則此方法在整個(gè)類加載過程中同步getClassLoadingLock()方法的結(jié)果腹殿。
  *
* @param name    binary name 二進(jìn)制文件名字独悴,其實(shí)就是類的全限定類名。
* @param resolve true 解析類
* @return 返回的是Class對(duì)象
* @throws ClassNotFoundException 如果類不存在锣尉,則拋出類未發(fā)現(xiàn)異常
*/
  protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
    synchronized (getClassLoadingLock(name)) {
        // 檢查要加載的類是不是已經(jīng)被加載了
        Class<?> c = findLoadedClass(name);
        // 沒有被加載過
        if (c == null) {
            long t0 = System.nanoTime();
            try {
                if (parent != null) {
                    // 如果父加載器不是BootStrapClassLoader刻炒,遞歸調(diào)用loadClass(name, false)
                    c = parent.loadClass(name, false);
                } else {
                    // BootStrapClassLoader加載器進(jìn)行加載
                    c = findBootstrapClassOrNull(name);
                }
            } catch (ClassNotFoundException e) {
                // 類未發(fā)現(xiàn)時(shí),報(bào)異常
            }
            if (c == null) {
                // 如果父類加載器未找到自沧,再調(diào)用本身(這個(gè)本身包括ext和app)的findClass(name)來查找類
                long t1 = System.nanoTime();
                c = findClass(name);
                // 定義類加載器坟奥,記錄數(shù)據(jù)
                sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
                sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
                sun.misc.PerfCounter.getFindClasses().increment();
            }
        }
        if (resolve) {
            resolveClass(c);
        }
        return c;
    }
  }

// 將一個(gè)byte數(shù)組轉(zhuǎn)換為Class類的實(shí)例,會(huì)先去讀取class文件暂幼,然后轉(zhuǎn)成Class類實(shí)例
protected final Class<?> defineClass(String name, byte[] b, int off, int len, ProtectionDomain protectionDomain) throws ClassFormatError {
    protectionDomain = preDefineClass(name, protectionDomain);
    String source = defineClassSourceLocation(protectionDomain);
    Class<?> c = defineClass1(name, b, off, len, protectionDomain, source);
    postDefineClass(c, protectionDomain);
    return c;
}

示例:
過程:假設(shè)我現(xiàn)在從類路徑下加載一個(gè)類A筏勒,
1:那么AppClassLoader會(huì)先查找是否加載過A,若有旺嬉,直接返回管行;
2:若沒有,去ExtClassLoader檢查是否加載過A邪媳,若有捐顷,直接返回;
3:若沒有雨效,去BootstrapClassLoader檢查是否加載過A迅涮,若有,直接返回徽龟;
4:若沒有叮姑,那就BootstrapClassLoader加載,若在E:\Java\jdk1.6\jre\lib*.jar下找到了指定名稱的類据悔,則加載传透,結(jié)束;
5:若沒找到极颓,BootstrapClassLoader加載失斨煅巍;
6:ExtClassLoader開始加載菠隆,若在E:\Java\jdk1.6\jre\lib\ext*.jar下找到了指定名稱的類兵琳,則加載狂秘,結(jié)束;
7:若沒找到躯肌,ExtClassLoader加載失斦叽骸;
8:AppClassLoader加載羡榴,若在類路徑下找到了指定名稱的類碧查,則加載,結(jié)束校仑;
9:若沒有找到忠售,拋出異常ClassNotFoundException

注意:
1:類的加載過程只有向上的雙親委托,沒有向下的查詢和加載迄沫,假設(shè)是ExtClassLoader在\Java\jdk1.8\jre\lib\ext*.jar下加載一個(gè)類稻扬,那么整個(gè)查詢與加載的過程與AppClassLoader無關(guān)。
2:假設(shè)A加載成功了羊瘩,那么該類就會(huì)緩存在當(dāng)前的類加載器實(shí)例對(duì)象C中泰佳,key是(A,C)(其中A是類的全類名尘吗,C是加載A的類加載器對(duì)象實(shí)例)逝她,value是對(duì)應(yīng)的java.lang.Class對(duì)象。
3:上述的加載示例中1睬捶、2黔宛、3都是從相應(yīng)的類加載器實(shí)例對(duì)象的緩存中進(jìn)行查找,進(jìn)行緩存的目的是為了同一個(gè)類不被加載兩次擒贸。

類加載過程中:檢查時(shí)(調(diào)用findLoadedClass(name)):從下向上檢查是否加載過指定名稱的類臀晃;加載時(shí)(loadClass(name, false)):從上向下加載該類。(在其中任何一個(gè)步驟成功之后介劫,都會(huì)中止類加載過程)

四:雙親委派機(jī)制的好處
假設(shè)自己編寫了一個(gè)java.lang.Object類徽惋,編譯后置于類路徑下,此時(shí)在系統(tǒng)中就有兩個(gè)Object類座韵,一個(gè)是rt.jar的险绘,一個(gè)是類路徑下的,在類加載的過程中誉碴,當(dāng)要按照全類名去加載Object類時(shí)宦棺,根據(jù)雙親委托,BootstrapClassLoader會(huì)加載rt.jar下的Object類翔烁,這時(shí)方法結(jié)束渺氧,即類路徑下的Object類就沒有加載了旨涝。這樣保證了系統(tǒng)中類不混亂蹬屹。

五:自定義類加載器
extends ClassLoader侣背,然后重寫父類的findClass方法。
注:我們自定義的類加載器沒有指定父加載器慨默,在JVM規(guī)范中不指定父類加載器的情況下贩耐,默認(rèn)采用系統(tǒng)類加載器即AppClassLoader作為其父加載器,所以在使用該自定義類加載器時(shí)厦取,需要加載的類不能在類路徑中潮太,否則的話根據(jù)雙親委派模型的原則,待加載的類會(huì)由系統(tǒng)類加載器加載虾攻,而不是自定義加載器加載铡买。如果一定想要把自定義加載器需要加載的類放在類路徑中, 就要把自定義類加載器的父加載器設(shè)置為null。

問:父類有那么多方法霎箍,為什么偏偏只重寫findClass方法奇钞?
因?yàn)镴dk已經(jīng)在loadClass()中幫我們實(shí)現(xiàn)了ClassLoader搜索類的算法,當(dāng)在loadClass方法中搜索不到類時(shí)漂坏,loadClass方法就會(huì)調(diào)用findClass方法來搜索類景埃,所以我們只需重寫該方法即可。如沒有特殊的要求顶别,一般不建議重寫loadClass搜索類的算法谷徙。

問:什么時(shí)候該使用自定義類加載器呢?
Java中默認(rèn)的三種類加載器都是有默認(rèn)加載路徑的驯绎。
當(dāng)需要的加載路徑不是默認(rèn)的三種類加載器的加載路徑時(shí)完慧,可以構(gòu)造自定義加載器,指定加載路徑条篷,.class可以是來自于磁盤骗随、內(nèi)存、網(wǎng)絡(luò)或者其它赴叹。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末鸿染,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子乞巧,更是在濱河造成了極大的恐慌涨椒,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,542評(píng)論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件绽媒,死亡現(xiàn)場(chǎng)離奇詭異蚕冬,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)是辕,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,822評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門囤热,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人获三,你說我怎么就攤上這事旁蔼∠撬眨” “怎么了?”我有些...
    開封第一講書人閱讀 163,912評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵棺聊,是天一觀的道長(zhǎng)伞租。 經(jīng)常有香客問我,道長(zhǎng)限佩,這世上最難降的妖魔是什么葵诈? 我笑而不...
    開封第一講書人閱讀 58,449評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮祟同,結(jié)果婚禮上作喘,老公的妹妹穿的比我還像新娘。我一直安慰自己晕城,他們只是感情好徊都,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,500評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著广辰,像睡著了一般暇矫。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上择吊,一...
    開封第一講書人閱讀 51,370評(píng)論 1 302
  • 那天李根,我揣著相機(jī)與錄音,去河邊找鬼几睛。 笑死房轿,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的所森。 我是一名探鬼主播囱持,決...
    沈念sama閱讀 40,193評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼焕济!你這毒婦竟也來了纷妆?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,074評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤晴弃,失蹤者是張志新(化名)和其女友劉穎掩幢,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體上鞠,經(jīng)...
    沈念sama閱讀 45,505評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡际邻,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,722評(píng)論 3 335
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了芍阎。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片世曾。...
    茶點(diǎn)故事閱讀 39,841評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖谴咸,靈堂內(nèi)的尸體忽然破棺而出轮听,到底是詐尸還是另有隱情肿轨,我是刑警寧澤,帶...
    沈念sama閱讀 35,569評(píng)論 5 345
  • 正文 年R本政府宣布蕊程,位于F島的核電站,受9級(jí)特大地震影響驼唱,放射性物質(zhì)發(fā)生泄漏藻茂。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,168評(píng)論 3 328
  • 文/蒙蒙 一玫恳、第九天 我趴在偏房一處隱蔽的房頂上張望辨赐。 院中可真熱鬧,春花似錦京办、人聲如沸掀序。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,783評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽不恭。三九已至,卻和暖如春财饥,著一層夾襖步出監(jiān)牢的瞬間换吧,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,918評(píng)論 1 269
  • 我被黑心中介騙來泰國(guó)打工钥星, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留沾瓦,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,962評(píng)論 2 370
  • 正文 我出身青樓谦炒,卻偏偏與公主長(zhǎng)得像贯莺,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子宁改,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,781評(píng)論 2 354

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

  • ClassLoader 即類加載器缕探,其作用是在JVM虛擬機(jī)中動(dòng)態(tài)的加載class文件。眾所周知还蹲,我們寫的Java應(yīng)...
    sunnyaxin閱讀 949評(píng)論 0 1
  • 提起熱修復(fù)以及插件化撕蔼,相信大家肯定不陌生,而無論是熱修復(fù)還是插件化秽誊,其理論依據(jù)就是Android 類加載機(jī)制鲸沮。 類...
    奔跑吧李博閱讀 916評(píng)論 0 10
  • 1.JVM運(yùn)行流程 JVM運(yùn)行流程如下圖所示: 2.JVM基本結(jié)構(gòu) JVM基本機(jī)構(gòu)包括:類加載器,執(zhí)行引擎,運(yùn)行時(shí)...
    landy8530閱讀 1,619評(píng)論 0 5
  • 1.簡(jiǎn)介 類加載器負(fù)責(zé)加載類。給定類的二進(jìn)制名厘惦,類加載器會(huì)嘗試定位或生成構(gòu)成類定義的數(shù)據(jù)嚷堡。典型的策略是將名稱轉(zhuǎn)換為...
    王偵閱讀 589評(píng)論 0 1
  • 其他有關(guān)插件化的文章歡迎大家觀閱插件化踩坑之路——Small和Atlas方案對(duì)比Android插件化基礎(chǔ)篇—— c...
    小之丶閱讀 1,848評(píng)論 1 7