類加載器

??不論人生多不幸火诸,聰明的人總會從中獲得一點利益抚官;不論人生多幸福哀军,愚蠢的人總覺得無限悲哀。 —— 拉·羅休弗克

??通過一個類的全限定名來獲取描述此類的二進制字節(jié)流這個動作放到Java虛擬機外部去實現(xiàn)球匕,以便讓應(yīng)用程序自身決定如何獲取所需要的類。實現(xiàn)這個動作的代碼模塊稱為"類加載器"帖烘。

類與類加載器

??對于任意一個類亮曹,都需要由加載它的類加載器和這個類本身一同確立其在Java虛擬機中的唯一性。更通俗一點的說法:比較兩個類是否"相等",只有在這兩個類是由同一個類加載器加載的前提下才有意義乾忱,否則讥珍,即使這兩個類來源于同一個class文件,被同一個虛擬機加載窄瘟,只要加載它們的類加載器不一樣衷佃,那這兩個類就必定不相等。

??這里的"相等"蹄葱,包括代表類的Class對象的equals()方法氏义、isAssignableForm()方法、isInstance()方法的返回結(jié)果图云,也包括使用instanceof關(guān)鍵字做對象所屬關(guān)系判斷等惯悠。

public class ClassLoaderTest {

    public static void main(String[] args) throws InstantiationException, IllegalAccessException, ClassNotFoundException {
        ClassLoader classLoader = new ClassLoader(){
            @Override
            public Class<?> loadClass(String name)
                    throws ClassNotFoundException {
                try {
                    String fileName = name.substring(name.lastIndexOf(".") + 1) + ".class";
                    InputStream is = getClass().getResourceAsStream(fileName);
                    if(null == is){
                        return super.loadClass(name);
                    }
                    byte[] b = new byte[is.available()];
                    is.read(b);
                    return defineClass(name, b, 0, b.length);
                } catch (IOException e) {
                    throw new ClassNotFoundException(name);
                }
            }
        };

        Object obj = classLoader.loadClass("com.xx.java.ClassLoaderTest").newInstance();
        System.out.println(obj.getClass());
        System.out.println(obj instanceof com.xx.java.ClassLoaderTest);
    }
}

運行結(jié)果:

class com.xx.java.ClassLoaderTest
false

??從運行結(jié)果來看,第一句返回的是class com.xx.java.ClassLoaderTest竣况,說明這個對象確實是com.xx.java.ClassLoaderTest的實例化對象克婶,而在用instanceof進行類所屬關(guān)系判定時,返回的結(jié)果是false丹泉,這是因為虛擬機中存在了兩個ClassLoaderTest情萤,一個是由系統(tǒng)應(yīng)用程序類加載器加載的,另外一個是由我們自定義的類加載器加載的摹恨,雖然來自于同一個class文件筋岛,但依然是兩個獨立的類。

雙親委派模型

??從Java虛擬機來講晒哄,只存在兩種類加載器睁宰,一種是啟動類加載器(Bootstrap ClassLoader),另一種就是所有其他的類加載器寝凌,并且全部繼承抽象類java.lang.ClassLoader柒傻。
??從Java開發(fā)人員來講,類加載器可分為以下三種類加載器:

  • 啟動類加載器(Bootstrap ClassLoader):該類加載器負責(zé)將存放在<JAVA_HOME>\lib目錄中的硫兰,或者被-Xbootclasspath參數(shù)所指定的路徑中的诅愚,并且可被虛擬機識別的類庫加載到虛擬機。
  • 擴展類加載器(Extension ClassLoader):該類加載器由sun.misc.Launcher$ExtClassLoader實現(xiàn)劫映,負責(zé)加載<JAVA_HOME>\lib\ext目錄中的违孝,或被java.ext.dirs系統(tǒng)變量所指定的路徑中的所有類庫。
  • 應(yīng)用程序類加載器(Application ClassLoader):該類加載器由sun.misc.Launcher$AppClassLoader實現(xiàn)泳赋。負責(zé)加載Classpath上所指定的類雌桑。
  • 自定義類加載器
    ??除了以上3種類加載器外,開發(fā)人員還可以自定義類加載器祖今。實現(xiàn)自定義類加載有兩種方式:重寫ClassLoader類的findClass()方法或loadClass()方法校坑。
  • 重寫loadClass()方法:
    ??如果要想在JVM的不同類加載器中保留具有相同全限定名的類拣技,那就要通過重寫loadClass來實現(xiàn)。實現(xiàn)方式是:首先通過用戶自定義的類加載器來判斷該類是否可加載耍目,如果可以加載就由自定義的類加載器進行加載膏斤,如果不能夠加載才交給父類加載器去加載。 這種情況下邪驮,就有可能有大量相同的類莫辨,被不同的自定義類加載器加載到JVM中
ClassLoader loadloader = new ClassLoader() {  
@Override  
public Class<?> loadClass(String name) throws ClassNotFoundException {  
        try {  
            String filename = name.replace('.', '/') + ".class";   
            InputStream is = getClass().getResourceAsStream(filename); 
            if (is == null) {  
                return super.loadClass(name);  
            }  
            byte[] bt = new byte[is.available()];  
            is.read(bt);  
            return defineClass(name, bt, 0, bt.length);  
        } catch (IOException e) {  
            e.printStackTrace(); 
        }  
        return super.loadClass(name);
    }  
} 
  • 重寫findClass()方法
    ??重寫findClass方法的自定義類毅访,首先會通過父類加載器進行加載沮榜,如果所有父類加載器都無法加載,再通過用戶自定義的findClass方法進行加載喻粹。如果父類加載器可以加載這個類或者當前類已經(jīng)存在于某個父類的容器中了蟆融,這個類是不會再次被加載的,此時用戶自定義的findClass方法就不會被執(zhí)行守呜,保證了相同全限定名的類是不會被重復(fù)加載到JVM中型酥。
ClassLoader findloader = new ClassLoader() {  
@Override  
public Class<?> findClass(String name) throws ClassNotFoundException {  
        try {  
            String filename = name.replace('.', '/') + ".class";   
            InputStream is = getClass().getResourceAsStream(filename); 
            if (is == null) {  
                return super.findClass(name);  
            }  
            byte[] bt = new byte[is.available()];  
            is.read(bt);  
            return defineClass(name, bt, 0, bt.length);  
        } catch (IOException e) {  
            e.printStackTrace(); 
        }  
        return super.findClass(name);
    }  
}

??下圖為雙親委派模型的工作示意圖:


雙親委派模型

??雙親委派模型的工作過程:當一個類加載器收到類加載請求,它首先將這個請求委派給父類加載器去完成查乒,自己不會去加載冕末,每一個層次的類加載器都是如此。只有當父類加載器無法完成加載請求時侣颂,子類加載器才會嘗試自己加載。
??使用雙親委派模型的好處在于Java類隨著它的類加載器一起具備了一種帶有優(yōu)先級的層次關(guān)系枪孩。例如類java.lang.Object憔晒,它存在在rt.jar中,無論哪一個類加載器要加載這個類蔑舞,最終都是委派給處于模型最頂端的Bootstrap ClassLoader進行加載拒担,因此Object類在程序的各種類加載器環(huán)境中都是同一個類。相反攻询,如果沒有雙親委派模型而是由各個類加載器自行加載的話从撼,如果用戶編寫了一個java.lang.Object的同名類并放在ClassPath中,那系統(tǒng)中將會出現(xiàn)多個不同的Object類钧栖。

雙親委派模型的系統(tǒng)實現(xiàn)

??在java.lang.ClassLoader的loadClass()方法中低零,先檢查是否已經(jīng)被加載過,若沒有加載則調(diào)用父類加載器的loadClass()方法拯杠,若父加載器為空則默認使用啟動類加載器作為父加載器掏婶。如果父加載失敗,則拋出ClassNotFoundException異常后潭陪,再調(diào)用自己的findClass()方法進行加載雄妥。

protected synchronized Class<?> loadClass(String name,boolean resolve)throws ClassNotFoundException{
    // First, check if the class has already been loaded
    Class c = findLoadedClass(name);
    if(c == null){
        try{
            if(parent != null){ // use parent classloader load class
                c = parent.loadClass(name,false);
            }else{ // use bootstrap classloader load class
                c = findBootstrapClassOrNull(name);
            }
        }catch(ClassNotFoundException e){
            //if throws the exception ,the father can not complete the load
        }
        if(c == null){// If still not found, then invoke findClass in order to find the class
            c = findClass(name);
        }
    }
    if(resolve){
        resolveClass(c);
    }
    return c;
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末最蕾,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子老厌,更是在濱河造成了極大的恐慌瘟则,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,734評論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件枝秤,死亡現(xiàn)場離奇詭異醋拧,居然都是意外死亡,警方通過查閱死者的電腦和手機宿百,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,931評論 3 394
  • 文/潘曉璐 我一進店門趁仙,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人垦页,你說我怎么就攤上這事雀费。” “怎么了痊焊?”我有些...
    開封第一講書人閱讀 164,133評論 0 354
  • 文/不壞的土叔 我叫張陵盏袄,是天一觀的道長。 經(jīng)常有香客問我薄啥,道長辕羽,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,532評論 1 293
  • 正文 為了忘掉前任垄惧,我火速辦了婚禮刁愿,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘到逊。我一直安慰自己铣口,他們只是感情好,可當我...
    茶點故事閱讀 67,585評論 6 392
  • 文/花漫 我一把揭開白布觉壶。 她就那樣靜靜地躺著脑题,像睡著了一般。 火紅的嫁衣襯著肌膚如雪铜靶。 梳的紋絲不亂的頭發(fā)上叔遂,一...
    開封第一講書人閱讀 51,462評論 1 302
  • 那天,我揣著相機與錄音争剿,去河邊找鬼已艰。 笑死,一個胖子當著我的面吹牛蚕苇,可吹牛的內(nèi)容都是我干的旗芬。 我是一名探鬼主播,決...
    沈念sama閱讀 40,262評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼捆蜀,長吁一口氣:“原來是場噩夢啊……” “哼疮丛!你這毒婦竟也來了幔嫂?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,153評論 0 276
  • 序言:老撾萬榮一對情侶失蹤誊薄,失蹤者是張志新(化名)和其女友劉穎履恩,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體呢蔫,經(jīng)...
    沈念sama閱讀 45,587評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡切心,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,792評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了片吊。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片绽昏。...
    茶點故事閱讀 39,919評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖俏脊,靈堂內(nèi)的尸體忽然破棺而出全谤,到底是詐尸還是另有隱情,我是刑警寧澤爷贫,帶...
    沈念sama閱讀 35,635評論 5 345
  • 正文 年R本政府宣布认然,位于F島的核電站,受9級特大地震影響漫萄,放射性物質(zhì)發(fā)生泄漏卷员。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,237評論 3 329
  • 文/蒙蒙 一腾务、第九天 我趴在偏房一處隱蔽的房頂上張望毕骡。 院中可真熱鬧,春花似錦岩瘦、人聲如沸挺峡。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,855評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至尤仍,卻和暖如春箫津,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背宰啦。 一陣腳步聲響...
    開封第一講書人閱讀 32,983評論 1 269
  • 我被黑心中介騙來泰國打工苏遥, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人赡模。 一個月前我還...
    沈念sama閱讀 48,048評論 3 370
  • 正文 我出身青樓田炭,卻偏偏與公主長得像,于是被迫代替她去往敵國和親漓柑。 傳聞我的和親對象是個殘疾皇子教硫,可洞房花燭夜當晚...
    茶點故事閱讀 44,864評論 2 354

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