類加載器與雙親委派模型

類加載器

加載類的開放性

類加載器(ClassLoader)是Java語言的一項創(chuàng)新拿霉,也是Java流行的一個重要原因。在類加載的第一階段“加載”過程中翔始,需要通過一個類的全限定名來獲取定義此類的二進制字節(jié)流尊浓,完成這個動作的代碼塊就是類加載器。這一動作是放在Java虛擬機外部去實現的进泼,以便讓應用程序自己決定如何獲取所需的類。

虛擬機規(guī)范并沒有指明二進制字節(jié)流要從一個Class文件獲取纤虽,或者說根本沒有指明從哪里獲取乳绕、怎樣獲取。這種開放使得Java在很多領域得到充分運用逼纸,例如:

  • 從ZIP包中讀取洋措,這很常見,成為JAR樊展,EAR呻纹,WAR格式的基礎
  • 從網絡中獲取,最典型的應用就是Applet
  • 運行時計算生成专缠,最典型的是動態(tài)代理技術,在java.lang.reflect.Proxy中淑仆,就是用了ProxyGenerator.generateProxyClass來為特定接口生成形式為“*$Proxy”的代理類的二進制字節(jié)流
  • 有其他文件生成涝婉,最典型的JSP應用,由JSP文件生成對應的Class類
    ……

類加載器與類的唯一性

類加載器雖然只用于實現類的加載動作蔗怠,但是對于任意一個類墩弯,都需要由加載它的類加載器和這個類本身共同確立其在Java虛擬機中的唯一性。通俗的說寞射,JVM中兩個類是否“相等”渔工,首先就必須是同一個類加載器加載的,否則桥温,即使這兩個類來源于同一個Class文件引矩,被同一個虛擬機加載,只要類加載器不同,那么這兩個類必定是不相等的旺韭。

這里的“相等”氛谜,包括代表類的Class對象的equals()方法、isAssignableFrom()方法区端、isInstance()方法的返回結果值漫,也包括使用instanceof關鍵字做對象所屬關系判定等情況。

以下代碼說明了不同的類加載器對instanceof關鍵字運算的結果的影響织盼。

package com.jvm.classloading;

import java.io.IOException;
import java.io.InputStream;

/**
 * 類加載器在類相等判斷中的影響
 * 
 * instanceof關鍵字
 * 
 */

public class ClassLoaderTest {
    public static void main(String[] args) throws Exception {
        // 自定義類加載器
        ClassLoader myLoader = 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 (is == null) {
                        return super.loadClass(fileName);
                    }
                    byte[] b = new byte[is.available()];
                    is.read(b);
                    return defineClass(name, b, 0, b.length);   
                } catch (IOException e) {
                    throw new ClassNotFoundException();
                }
            }
        };

        // 使用ClassLoaderTest的類加載器加載本類
        Object obj1 = ClassLoaderTest.class.getClassLoader().loadClass("com.jvm.classloading.ClassLoaderTest").newInstance();
        System.out.println(obj1.getClass());
        System.out.println(obj1 instanceof com.jvm.classloading.ClassLoaderTest);

        // 使用自定義類加載器加載本類
        Object obj2 = myLoader.loadClass("com.jvm.classloading.ClassLoaderTest").newInstance();
        System.out.println(obj2.getClass());
        System.out.println(obj2 instanceof com.jvm.classloading.ClassLoaderTest);
    }
}

輸出結果:

class com.jvm.classloading.ClassLoaderTest
true
class com.jvm.classloading.ClassLoaderTest
false

myLoader是自定義的類加載器杨何,可以用來加載與自己在同一路徑下的Class文件。main函數的第一部分使用系統加載主類ClassLoaderTest的類加載器加載ClassLoaderTest沥邻,輸出顯示晚吞,obj1的所屬類型檢查正確,這是虛擬機中有2個ClassLoaderTest類谋国,一個是主類槽地,另一個是main()方法中加載的類,由于這兩個類使用同一個類加載器加載并且來源于同一個Class文件芦瘾,因此這兩個類是完全相同的捌蚊。

第二部分使用自定義的類加載器加載ClassLoaderTest,class com.jvm.classloading.ClassLoderTest顯示近弟,obj2確實是類com.jvm.classloading.ClassLoaderTest實例化出來的對象缅糟,但是第二句輸出false。此時虛擬機中有3個ClassLoaderTest類祷愉,由于第3個類的類加載器與前面2個類加載器不同窗宦,雖然來源于同一個Class文件,但它是一個獨立的類二鳄,所屬類型檢查是返回結果自然是false赴涵。

雙親委派模型

類加載器種類

從Java虛擬機的角度來說,只存在兩種不同的類加載器:一種是啟動類加載器(Bootstrap ClassLoader)订讼,這個類加載器使用C++語言實現(HotSpot虛擬機中)髓窜,是虛擬機自身的一部分;另一種就是所有其他的類加載器欺殿,這些類加載器都有Java語言實現寄纵,獨立于虛擬機外部,并且全部繼承自java.lang.ClassLoader脖苏。

從開發(fā)者的角度程拭,類加載器可以細分為:

  • 啟動(Bootstrap)類加載器:負責將 Java_Home/lib下面的類庫加載到內存中(比如rt.jar)。由于引導類加載器涉及到虛擬機本地實現細節(jié)棍潘,開發(fā)者無法直接獲取到啟動類加載器的引用恃鞋,所以不允許直接通過引用進行操作崖媚。

  • 標準擴展(Extension)類加載器:是由 Sun 的 ExtClassLoader(sun.misc.Launcher$ExtClassLoader)實現的。它負責將Java_Home /lib/ext或者由系統變量 java.ext.dir指定位置中的類庫加載到內存中山宾。開發(fā)者可以直接使用標準擴展類加載器至扰。

  • 應用程序(Application)類加載器:是由 Sun 的 AppClassLoader(sun.misc.Launcher$AppClassLoader)實現的。它負責將系統類路徑(CLASSPATH)中指定的類庫加載到內存中资锰。開發(fā)者可以直接使用系統類加載器敢课。由于這個類加載器是ClassLoader中的getSystemClassLoader()方法的返回值,因此一般稱為系統(System)加載器绷杜。

除此之外直秆,還有自定義的類加載器,它們之間的層次關系被稱為類加載器的雙親委派模型鞭盟。該模型要求除了頂層的啟動類加載器外圾结,其余的類加載器都應該有自己的父類加載器,而這種父子關系一般通過組合(Composition)關系來實現齿诉,而不是通過繼承(Inheritance)筝野。

類加載器的雙親委派模型

雙親委派模型

雙親委派模型過程

某個特定的類加載器在接到加載類的請求時,首先將加載任務委托給父類加載器粤剧,依次遞歸歇竟,如果父類加載器可以完成類加載任務,就成功返回抵恋;只有父類加載器無法完成此加載任務時焕议,才自己去加載。

使用雙親委派模型的好處在于Java類隨著它的類加載器一起具備了一種帶有優(yōu)先級的層次關系弧关。例如類java.lang.Object盅安,它存在在rt.jar中,無論哪一個類加載器要加載這個類世囊,最終都是委派給處于模型最頂端的Bootstrap ClassLoader進行加載别瞭,因此Object類在程序的各種類加載器環(huán)境中都是同一個類。相反茸习,如果沒有雙親委派模型而是由各個類加載器自行加載的話畜隶,如果用戶編寫了一個java.lang.Object的同名類并放在ClassPath中,那系統中將會出現多個不同的Object類号胚,程序將混亂。因此浸遗,如果開發(fā)者嘗試編寫一個與rt.jar類庫中重名的Java類猫胁,可以正常編譯,但是永遠無法被加載運行跛锌。

雙親委派模型的系統實現

在java.lang.ClassLoader的loadClass()方法中弃秆,先檢查是否已經被加載過届惋,若沒有加載則調用父類加載器的loadClass()方法,若父加載器為空則默認使用啟動類加載器作為父加載器菠赚。如果父加載失敗脑豹,則拋出ClassNotFoundException異常后,再調用自己的findClass()方法進行加載衡查。

protected synchronized Class<?> loadClass(String name,boolean resolve)throws ClassNotFoundException{
    //check the class has been loaded or not
    Class c = findLoadedClass(name);
    if(c == null){
        try{
            if(parent != null){
                c = parent.loadClass(name,false);
            }else{
                c = findBootstrapClassOrNull(name);
            }
        }catch(ClassNotFoundException e){
            //if throws the exception ,the father can not complete the load
        }
        if(c == null){
            c = findClass(name);
        }
    }
    if(resolve){
        resolveClass(c);
    }
    return c;
}

注意瘩欺,雙親委派模型是Java設計者推薦給開發(fā)者的類加載器的實現方式,并不是強制規(guī)定的拌牲。大多數的類加載器都遵循這個模型俱饿,但是JDK中也有較大規(guī)模破壞雙親模型的情況,例如線程上下文類加載器(Thread Context ClassLoader)的出現塌忽,具體分析可以參見周志明著《深入理解Java虛擬機》拍埠。

原文:https://blog.csdn.net/u011080472/article/details/51332866

?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市土居,隨后出現的幾起案子枣购,更是在濱河造成了極大的恐慌,老刑警劉巖擦耀,帶你破解...
    沈念sama閱讀 222,183評論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件棉圈,死亡現場離奇詭異,居然都是意外死亡埂奈,警方通過查閱死者的電腦和手機迄损,發(fā)現死者居然都...
    沈念sama閱讀 94,850評論 3 399
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來账磺,“玉大人芹敌,你說我怎么就攤上這事】蹇梗” “怎么了氏捞?”我有些...
    開封第一講書人閱讀 168,766評論 0 361
  • 文/不壞的土叔 我叫張陵,是天一觀的道長冒版。 經常有香客問我液茎,道長,這世上最難降的妖魔是什么辞嗡? 我笑而不...
    開封第一講書人閱讀 59,854評論 1 299
  • 正文 為了忘掉前任捆等,我火速辦了婚禮,結果婚禮上续室,老公的妹妹穿的比我還像新娘栋烤。我一直安慰自己,他們只是感情好挺狰,可當我...
    茶點故事閱讀 68,871評論 6 398
  • 文/花漫 我一把揭開白布明郭。 她就那樣靜靜地躺著买窟,像睡著了一般。 火紅的嫁衣襯著肌膚如雪薯定。 梳的紋絲不亂的頭發(fā)上始绍,一...
    開封第一講書人閱讀 52,457評論 1 311
  • 那天,我揣著相機與錄音话侄,去河邊找鬼亏推。 笑死,一個胖子當著我的面吹牛满葛,可吹牛的內容都是我干的径簿。 我是一名探鬼主播,決...
    沈念sama閱讀 40,999評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼嘀韧,長吁一口氣:“原來是場噩夢啊……” “哼篇亭!你這毒婦竟也來了?” 一聲冷哼從身側響起锄贷,我...
    開封第一講書人閱讀 39,914評論 0 277
  • 序言:老撾萬榮一對情侶失蹤译蒂,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后谊却,有當地人在樹林里發(fā)現了一具尸體柔昼,經...
    沈念sama閱讀 46,465評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 38,543評論 3 342
  • 正文 我和宋清朗相戀三年炎辨,在試婚紗的時候發(fā)現自己被綠了捕透。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,675評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡碴萧,死狀恐怖乙嘀,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情破喻,我是刑警寧澤虎谢,帶...
    沈念sama閱讀 36,354評論 5 351
  • 正文 年R本政府宣布,位于F島的核電站曹质,受9級特大地震影響婴噩,放射性物質發(fā)生泄漏。R本人自食惡果不足惜羽德,卻給世界環(huán)境...
    茶點故事閱讀 42,029評論 3 335
  • 文/蒙蒙 一几莽、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧宅静,春花似錦银觅、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,514評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至匀伏,卻和暖如春洒忧,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背够颠。 一陣腳步聲響...
    開封第一講書人閱讀 33,616評論 1 274
  • 我被黑心中介騙來泰國打工熙侍, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人履磨。 一個月前我還...
    沈念sama閱讀 49,091評論 3 378
  • 正文 我出身青樓蛉抓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親剃诅。 傳聞我的和親對象是個殘疾皇子巷送,可洞房花燭夜當晚...
    茶點故事閱讀 45,685評論 2 360

推薦閱讀更多精彩內容