認(rèn)識(shí)類加載器


類加載器
在加載階段:通過一個(gè)類的全限定名來(lái)獲取其定義的二進(jìn)制字節(jié)流這一步會(huì)在虛擬機(jī)外部實(shí)現(xiàn)李破,以便讓應(yīng)用程序自己選擇所需要的類瘪弓,實(shí)現(xiàn)這個(gè)動(dòng)作的代碼模塊就成為:“類加載器”垫蛆。

類加載器雖然只用于實(shí)現(xiàn)類的加載動(dòng)作,但還有一些其他的作用腺怯。
每個(gè)類加載器都擁有一個(gè)獨(dú)立的類名稱空間月褥,因此如果要比較兩個(gè)類是否相等,必須要保證這兩個(gè)類是由同一個(gè)類加載器加載的前提下才能進(jìn)行確定瓢喉,否則即便是同一個(gè)Class文件的兩個(gè)類宁赤,被同一個(gè)虛擬機(jī)加載,但是加載它們的類加載器不同栓票,那么這兩個(gè)類也是不同的决左。

從Java虛擬機(jī)的角度來(lái)講,只有兩種不同的類加載器:
①啟動(dòng)類加載器(Bootstrap ClassLoader)走贪,這個(gè)類加載器是由C語(yǔ)言編寫的佛猛,是屬于虛擬機(jī)的一部分;
②所有其他的類加載器坠狡,這些類加載器都屬于Java語(yǔ)言編寫的继找,獨(dú)立于虛擬機(jī)外部,并且全部繼承抽象類Java.lang.ClassLoader

從Java角度來(lái)看逃沿,類加載器還可以劃分的細(xì)致一點(diǎn)
①啟動(dòng)類加載器(Bootstrap ClassLoader):負(fù)責(zé)加載存放在<JAVA_HOME>\lib目錄中婴渡,或被 -Xbootclasspath參數(shù)指定的路徑中的,并且能被虛擬機(jī)識(shí)別的類庫(kù)(如rt.jar凯亮,所有的java.開頭的類均被 BootstrapClassLoader加載)边臼。啟動(dòng)類加載器是無(wú)法被Java程序直接引用的。

②擴(kuò)展類加載器(Extension ClassLoader):該加載器由 sun.misc.Launcher$ExtClassLoader實(shí)現(xiàn)假消,它負(fù)責(zé)加載<JAVA_HOME>\lib\ext目錄中柠并,或者由 java.ext.dirs系統(tǒng)變量指定的路徑中的所有類庫(kù)(如javax.開頭的類),開發(fā)者可以直接使用擴(kuò)展類加載器富拗。

③應(yīng)用程序類加載器(Application ClassLoader):該類加載器由 sun.misc.Launcher$AppClassLoader來(lái)實(shí)現(xiàn)臼予,是ClassLoader中g(shù)etSystemClassLoader()方法的返回值,一般稱為系統(tǒng)類加載器啃沪。它負(fù)責(zé)加載用戶類路徑(ClassPath)所指定的類粘拾,開發(fā)者可以直接使用該類加載器,如果應(yīng)用程序中沒有自定義過自己的類加載器谅阿,一般情況下這個(gè)就是程序中默認(rèn)的類加載器半哟。

我們看一個(gè)尋找類加載器的小示例

public class Client {
   public static void main(String[] args) {
      ClassLoader loader = Thread.currentThread().getContextClassLoader();
      System.out.println(loader); //①
      System.out.println(loader.getParent());  //②
      System.out.println(loader.getParent().getParent());  //③
   }
}
-----output-----
sun.misc.Launcher$AppClassLoader@18b4aac2
sun.misc.Launcher$ExtClassLoader@46fbb2c1
null

①是應(yīng)用程序類加載器(Application ClassLoader),它的父類②是擴(kuò)展類加載器(Extension ClassLoader)签餐,③并沒有返回?cái)U(kuò)展類加載器的父類啟動(dòng)類加載器寓涨,而是返回null,這是因?yàn)閱?dòng)類加載器(Bootstrap ClassLoader)是由C語(yǔ)言編寫氯檐,找不到一個(gè)確定的返回父Loader的方式戒良,于是就返回null。

應(yīng)用程序都是由這三種類加載器互相配合進(jìn)行加載的冠摄,如果有必要糯崎,我們還可以加入自定義的類加載器。因?yàn)镴VM自帶的ClassLoader只是懂得從本地文件系統(tǒng)加載標(biāo)準(zhǔn)的java class文件河泳,因此如果編寫了自己的ClassLoader沃呢,便可以做到如下幾點(diǎn):

  • 1、在執(zhí)行非置信代碼之前拆挥,自動(dòng)驗(yàn)證數(shù)字簽名薄霜。
  • 2、動(dòng)態(tài)地創(chuàng)建符合用戶特定需要的定制化構(gòu)建類纸兔。
  • 3惰瓜、從特定的場(chǎng)所取得java class,例如數(shù)據(jù)庫(kù)中和網(wǎng)絡(luò)中汉矿。

這幾種類加載器的層次關(guān)系如下圖所示崎坊,該圖所展示的層次關(guān)系稱為“雙親委派模型”:

雙親委派模型

注:這里父類加載器并不是通過繼承關(guān)系來(lái)實(shí)現(xiàn)的,而是采用組合實(shí)現(xiàn)的洲拇。

雙親委派模型(Parents Delegation Model)
上圖中展示的就是雙親委派模型關(guān)系奈揍,要求最頂層必須是啟動(dòng)類加載器,除此之外每個(gè)類加載器都必須要有自己的父類加載器赋续,這里的父子類并不是以繼承的關(guān)系實(shí)現(xiàn)打月,而是以組合的方式復(fù)用父加載器的代碼。

雙親委派模型的工作過程
如果一個(gè)類加載器收到了類加載的請(qǐng)求蚕捉,它首先不會(huì)自己去嘗試加載這個(gè)類奏篙,而是把請(qǐng)求委托給父加載器去完成,依次向上迫淹,因此秘通,所有的類加載請(qǐng)求最終都應(yīng)該被傳遞到頂層的啟動(dòng)類加載器中,只有當(dāng)父加載器在它的搜索范圍中沒有找到所需的類時(shí)敛熬,即無(wú)法完成該加載肺稀,子加載器才會(huì)嘗試自己去加載該類。

雙親委派機(jī)制:

  • 1应民、當(dāng) AppClassLoader加載一個(gè)class時(shí)话原,它首先不會(huì)自己去嘗試加載這個(gè)類夕吻,而是把類加載請(qǐng)求委派給父類加載器ExtClassLoader去完成。
  • 2繁仁、當(dāng) ExtClassLoader加載一個(gè)class時(shí)涉馅,它首先也不會(huì)自己去嘗試加載這個(gè)類,而是把類加載請(qǐng)求委派給BootStrapClassLoader```去完成黄虱。
  • 3稚矿、如果 BootStrapClassLoader加載失敗(例如在 $JAVA_HOME/jre/lib里未查找到該class)捻浦,會(huì)使用 ExtClassLoader來(lái)嘗試加載晤揣;
  • 4、若ExtClassLoader也加載失敗朱灿,則會(huì)使用 AppClassLoader來(lái)加載昧识,如果 AppClassLoader也加載失敗,則會(huì)報(bào)出異常 ClassNotFoundException盗扒。

雙親委派模型意義
①系統(tǒng)類防止內(nèi)存中出現(xiàn)多份同樣的字節(jié)碼
②保證Java程序安全穩(wěn)定運(yùn)行

實(shí)現(xiàn)雙親委派模型的代碼都集中在 java.lang.ClassLoader 的 loadClass( )方法中滞诺,具體源碼如下:

public Class<?> loadClass(String name) throws ClassNotFoundException {
    return loadClass(name, false);
}

protected Class<?> loadClass(String name, boolean resolve)
    throws ClassNotFoundException
{
    synchronized (getClassLoadingLock(name)) {
     // 首先判斷該類型是否已經(jīng)被加載
        Class<?> c = findLoadedClass(name);
        if (c == null) {
        //如果沒有被加載,就委托給父類加載或者委派給啟動(dòng)類加載器加載
            long t0 = System.nanoTime();
            try {
                if (parent != null) {
                //如果存在父類加載器环疼,就委派給父類加載器加載
                    c = parent.loadClass(name, false);
                } else {
            //如果不存在父類加載器习霹,就檢查是否是由啟動(dòng)類加載器加載的類,通過調(diào)用本地方法native Class findBootstrapClass(String name)
                    c = findBootstrapClassOrNull(name);
                }
            } catch (ClassNotFoundException e) {
                // 如果父類加載器拋出 ClassNotFoundException炫隶,那么說明父類加載器無(wú)法完成加載請(qǐng)求
            }
            if (c == null) {
                // 在父類加載器無(wú)法完成加載時(shí)淋叶,調(diào)用自身的加載功能 findClass(name)
                long t1 = System.nanoTime();
                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;
    }
}

如果我們需要自定義自己的類加載器,從上面對(duì) loadClass方法來(lái)分析來(lái)看伪阶,我們只需要重寫 findClass 方法即可煞檩。

擴(kuò)展閱讀
JVM類加載機(jī)制

  • 全盤負(fù)責(zé),當(dāng)一個(gè)類加載器負(fù)責(zé)加載某個(gè)Class時(shí)栅贴,該Class所依賴的和引用的其他Class也將由該類加載器負(fù)責(zé)載入斟湃,除非顯示使用另外一個(gè)類加載器來(lái)載入
  • 父類委托,先讓父類加載器試圖加載該類檐薯,只有在父類加載器無(wú)法加載該類時(shí)才嘗試從自己的類路徑中加載該類
  • 緩存機(jī)制凝赛,緩存機(jī)制將會(huì)保證所有加載過的Class都會(huì)被緩存,當(dāng)程序中需要使用某個(gè)Class時(shí)坛缕,類加載器先從緩存區(qū)尋找該Class墓猎,只有緩存區(qū)不存在,系統(tǒng)才會(huì)讀取該類對(duì)應(yīng)的二進(jìn)制數(shù)據(jù)赚楚,并將其轉(zhuǎn)換成Class對(duì)象毙沾,存入緩存區(qū)。這就是為什么修改了Class后宠页,必須重啟JVM左胞,程序的修改才會(huì)生效

Class.forName()和ClassLoader.loadClass()區(qū)別

  • Class.forName():將類的.class文件加載到j(luò)vm中之外寇仓,還會(huì)對(duì)類進(jìn)行解釋,執(zhí)行類中的static塊烤宙;
  • ClassLoader.loadClass():只干一件事情遍烦,就是將.class文件加載到j(luò)vm中,不會(huì)執(zhí)行static中的內(nèi)容,只有在newInstance才會(huì)去執(zhí)行static塊门烂。
  • Class.forName(name,initialize,loader)帶參函數(shù)也可控制是否加載static塊乳愉。并且只有調(diào)用了newInstance()方法采用調(diào)用構(gòu)造函數(shù)兄淫,創(chuàng)建類的對(duì)象 屯远。

參考文章:https://mp.weixin.qq.com/s?__biz=MzI4NDY5Mjc1Mg==&mid=2247483934&idx=1&sn=41c46eceb2add54b7cde9eeb01412a90&chksm=ebf6da61dc81537721d36aadb5d20613b0449762842f9128753e716ce5fefe2b659d8654c4e8&scene=21#wechat_redirect

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市捕虽,隨后出現(xiàn)的幾起案子慨丐,更是在濱河造成了極大的恐慌,老刑警劉巖泄私,帶你破解...
    沈念sama閱讀 207,113評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件房揭,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡晌端,警方通過查閱死者的電腦和手機(jī)捅暴,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評(píng)論 2 381
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)咧纠,“玉大人蓬痒,你說我怎么就攤上這事∑岣幔” “怎么了梧奢?”我有些...
    開封第一講書人閱讀 153,340評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)演痒。 經(jīng)常有香客問我亲轨,道長(zhǎng),這世上最難降的妖魔是什么鸟顺? 我笑而不...
    開封第一講書人閱讀 55,449評(píng)論 1 279
  • 正文 為了忘掉前任惦蚊,我火速辦了婚禮,結(jié)果婚禮上讯嫂,老公的妹妹穿的比我還像新娘养筒。我一直安慰自己,他們只是感情好端姚,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,445評(píng)論 5 374
  • 文/花漫 我一把揭開白布晕粪。 她就那樣靜靜地躺著,像睡著了一般渐裸。 火紅的嫁衣襯著肌膚如雪巫湘。 梳的紋絲不亂的頭發(fā)上装悲,一...
    開封第一講書人閱讀 49,166評(píng)論 1 284
  • 那天,我揣著相機(jī)與錄音尚氛,去河邊找鬼诀诊。 笑死,一個(gè)胖子當(dāng)著我的面吹牛阅嘶,可吹牛的內(nèi)容都是我干的属瓣。 我是一名探鬼主播,決...
    沈念sama閱讀 38,442評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼讯柔,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼抡蛙!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起魂迄,我...
    開封第一講書人閱讀 37,105評(píng)論 0 261
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤粗截,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后捣炬,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體熊昌,經(jīng)...
    沈念sama閱讀 43,601評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,066評(píng)論 2 325
  • 正文 我和宋清朗相戀三年湿酸,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了婿屹。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,161評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡推溃,死狀恐怖昂利,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情美莫,我是刑警寧澤页眯,帶...
    沈念sama閱讀 33,792評(píng)論 4 323
  • 正文 年R本政府宣布,位于F島的核電站厢呵,受9級(jí)特大地震影響窝撵,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜襟铭,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,351評(píng)論 3 307
  • 文/蒙蒙 一碌奉、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧寒砖,春花似錦赐劣、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,352評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至漠嵌,卻和暖如春咐汞,著一層夾襖步出監(jiān)牢的瞬間盖呼,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,584評(píng)論 1 261
  • 我被黑心中介騙來(lái)泰國(guó)打工化撕, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留几晤,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,618評(píng)論 2 355
  • 正文 我出身青樓植阴,卻偏偏與公主長(zhǎng)得像蟹瘾,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子掠手,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,916評(píng)論 2 344

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