Java中的類加載器

首先聲明霹粥,我是因為看了ImportNew網(wǎng)站上的幾篇關(guān)于類加載器的文章之后兼都,才萌生了寫這篇文章的想法旨涝。所以在寫這篇文章時漂羊,參考了該網(wǎng)站上的幾篇相關(guān)的文章驾锰,但是加入了很多自己的理解,絕對不是抄襲走越。

從java的動態(tài)性到類加載機制

我們知道椭豫,java是一種動態(tài)語言。那么怎樣理解這個“動態(tài)”呢旨指?或者說一門語言具備了什么特性赏酥,才能稱之為動態(tài)語言呢?對于java谆构,我是這樣理解的裸扶。

我們都知道JVM(java虛擬機)執(zhí)行的不是本地機器碼指令,而是執(zhí)行一種稱之為字節(jié)碼的指令(存在于class文件中)搬素。這就要求虛擬機在真正執(zhí)行字節(jié)碼之前呵晨,先把相關(guān)的class文件加載到內(nèi)存中。虛擬機不是一次性加載所有需要的class文件熬尺,因為它在執(zhí)行的時候根本不會知道以后會用到哪些class文件摸屠。它是每用到一個類,就會在運行時“動態(tài)地”加載和這個類相關(guān)的class文件粱哼。這就是java被稱之為動態(tài)性語言的根本原因季二。除了動態(tài)加載類之外,還會動態(tài)的初始化類皂吮,對類進行動態(tài)鏈接戒傻。動態(tài)初始化和動態(tài)鏈接放在其他文章中進行介紹。本文中只關(guān)心類的加載蜂筹。

在JVM中負(fù)責(zé)對類進行加載的正是本文要介紹的類加載器(ClassLoader)需纳,所以,類加載器是JVM不可或缺的重要組件艺挪。

java中的類加載器及類加載器工作原理

java中(指的是javase)有三種類加載器不翩。每個類加載器在創(chuàng)建的時候已經(jīng)指定他們對應(yīng)的目錄, 也就是說每個類加載器去哪里加載類是確定的兵扬,我認(rèn)為在ClassLoader類中應(yīng)該會有g(shù)etTargetPath()之類的方法, 得到他們對應(yīng)的路徑,找了找jdk的文檔,發(fā)現(xiàn)是沒有的口蝠。以下是這三種類加載器和他們對應(yīng)的路徑:

* AppClassLoader ?-- ? 加載classpath指定的路徑中的類

* ExtClassLoader ? -- ? 加載jre/lib/ext目錄下或者java.ext.dirs系統(tǒng)屬性定義的目錄下的類

* BootStrap ? ? ? ? ? -- ? 加載JRE/lib/rt.jar中的類

那么類加載器是如何工作的呢器钟?可以參看jdk中ClassLoader類的源碼。這個類的實現(xiàn)使用了模板方法模式妙蔗,首先是loadClass方法來加載類傲霸,loadClass方法又調(diào)用了findClass方法,該方法讀取并返回類文件的數(shù)據(jù)眉反,findClass方法返回后昙啄,loadClass方法繼續(xù)調(diào)用defineClass方法,將返回的數(shù)據(jù)加工成虛擬機運行時可識別的類型信息寸五。所以梳凛,我們?nèi)绻_發(fā)自己的類加載器,只需要繼承jdk中的ClassLoader類梳杏,并覆蓋findClass方法就可以了韧拒,剩下的而工作,父類會完成十性。其他java平臺有的根據(jù)自己的需求叛溢,實現(xiàn)了自己特定的類加載器,例如javaee平臺中的tomcat服務(wù)器烁试,android平臺中的dalvik虛擬機也定義了自己的類加載器雇初。

虛擬機加載類有兩種方式,一種方式就是上面提到的ClassLoader.loadClass()方法减响,另一種是使用反射API,Class.forName()方法郭怪,其實Class.forName()方法內(nèi)部也是使用的ClassLoader支示。Class類中forName方法的實現(xiàn)如下:

[java]view plaincopy

publicstaticClass?forName(String?name,booleaninitialize,

ClassLoader?loader)

throwsClassNotFoundException

{

if(loader?==null)?{

SecurityManager?sm?=?System.getSecurityManager();

if(sm?!=null)?{

ClassLoader?ccl?=?ClassLoader.getCallerClassLoader();

if(ccl?!=null)?{

sm.checkPermission(

SecurityConstants.GET_CLASSLOADER_PERMISSION);

}

}

}

returnforName0(name,?initialize,?loader);

}

/**?Called?after?security?checks?have?been?made.?*/

privatestaticnativeClass?forName0(String?name,booleaninitialize,

ClassLoader?loader)

throwsClassNotFoundException;

類加載器的三個特性

類加載器有三個特性,分別為委派鄙才,可見性和單一性颂鸿,其他文章上對這三個特性的介紹如下:

* 委托機制是指將加載一個類的請求交給父類加載器,如果這個父類加載器不能夠找到或者加載這個類攒庵,那么再加載它嘴纺。

* 可見性的原理是子類的加載器可以看見所有的父類加載器加載的類,而父類加載器看不到子類加載器加載的類浓冒。

* 單一性原理是指僅加載一個類一次栽渴,這是由委托機制確保子類加載器不會再次加載父類加載器加載過的類。

其中稳懒,委派機制是基礎(chǔ)闲擦,在其他資料中也把這種機制叫做類加載器的雙親委派模型,其實說的是同一個意思∈洌可加性和單一性是依賴于委派機制的纯路。

以下代碼測試類加載器的委派機制:

[java]view plaincopy

ClassLoader?appClassLoader?=?ClassLoaderTest.class.getClassLoader();

System.out.println(appClassLoader);//sun.misc.Launcher$AppClassLoader@19821f

ClassLoader?extClassLoader?=?appClassLoader.getParent();

System.out.println(extClassLoader);//sun.misc.Launcher$ExtClassLoader@addbf1

//AppClassLoader的父加載器是ExtClassLoader

System.out.println(extClassLoader.getParent());//null

//ExtClassLoader的父加載器是null,?也就是BootStrap,這是由c語言實現(xiàn)的

由打印結(jié)果可知,加載我們自己編寫的類的加載器是AppClassLoader寞忿,AppClassLoader的父加載器是ExtClassLoader驰唬,在而ExtClassLoader的父加載器返回結(jié)果為null,這說明他的附加載器是BootStrap腔彰,這個加載器是和虛擬機緊密聯(lián)系在一起的定嗓,在虛擬機啟動時,就會加載jdk中的類萍桌。它是由C實現(xiàn)的宵溅,沒有對應(yīng)的java對象,所以返回null上炎。但是在邏輯上恃逻,BootStrap仍是ExtClassLoader的父加載器。也就是說每當(dāng)ExtClassLoader加載一個類時藕施,總會委托給BootStrap加載寇损。

系統(tǒng)類加載器和線程上下文類加載器

在java中,還存在兩個概念裳食,分別是系統(tǒng)類加載器和線程上下文類加載器矛市。

其實系統(tǒng)類加載器就是AppClassLoader應(yīng)用程序類加載器,它兩個值得是同一個加載器,以下代碼可以驗證:

[java]view plaincopy

ClassLoader?appClassLoader?=?ClassLoaderTest.class.getClassLoader();

System.out.println(appClassLoader);//sun.misc.Launcher$AppClassLoader@19821f

ClassLoader?sysClassLoader?=?ClassLoader.getSystemClassLoader();

System.out.println(sysClassLoader);//sun.misc.Launcher$AppClassLoader@19821f

//由上面的驗證可知,?應(yīng)用程序類加載器和系統(tǒng)類加載器是相同的,?因為地址是一樣的

這兩個類加載器對應(yīng)的輸出胳泉,不僅類名相同爹脾,連對象的哈希值都是一樣的,這充分說明系統(tǒng)類加載器和應(yīng)用程序類加載器不僅是同一個類找田,更是同一個類的同一個對象。

每個線程都會有一個上下文類加載器,由于在線程執(zhí)行時加載用到的類,默認(rèn)情況下是父線程的上下文類加載器, 也就是AppClassLoader着憨。

[java]view plaincopy

newThread(newRunnable()?{

@Override

publicvoidrun()?{

ClassLoader?threadcontextClassLosder?=?Thread.currentThread().getContextClassLoader();

System.out.println(threadcontextClassLosder);//sun.misc.Launcher$AppClassLoader@19821f

}

}).start();

這個子線程在執(zhí)行時打印的信息為sun.misc.Launcher$AppClassLoader@19821f墩衙,可以看到和主線程中的AppClassLoader是同一個對象(哈希值相同)。

也可以為線程設(shè)置特定的類加載器甲抖,這樣的話漆改,線程在執(zhí)行時就會使用這個特定的類加載器來加載使用到的類。如下代碼:

[java]view plaincopy

Thread?th?=newThread(newRunnable()?{

@Override

publicvoidrun()?{

ClassLoader?threadcontextClassLosder?=?Thread.currentThread().getContextClassLoader();

System.out.println(threadcontextClassLosder);//jg.zhang.java.testclassloader.ClassLoaderTest$3@1b67f74

}

});

th.setContextClassLoader(newClassLoader()?{});

th.start();

在線程運行之前准谚,為它設(shè)置了一個匿名內(nèi)部類的類加載器對象挫剑,線程運行時,輸出的信息為:jg.zhang.java.testclassloader.ClassLoaderTest$3@1b67f74氛魁,也就是我們設(shè)置的那個類加載器對象暮顺。

類加載器的可見性

下面驗證類加載器的可見性厅篓,也就是?子類的加載器可以看見所有的父類加載器加載的類,而父類加載器看不到子類加載器加載的類捶码。

以下代碼使用父加載器ExtClassLoader加載子加載器AppClassLoader路徑下的類羽氮,由輸出可知,是不可能實現(xiàn)的惫恼。

[java]view plaincopy

try{

Class.forName("jg.zhang.java.testConcurrent.Person",true,

ClassLoaderTest.class.getClassLoader().getParent());

System.out.println("1?--?類被加載");

}catch(ClassNotFoundException?e)?{

//e.printStackTrace();

System.out.println("1?--?未找到類");

}

輸出為 :1 -- 未找到類 档押。說明拋出了ClassNotFoundException異常。原因是讓ExtClassLoader加載 jg.zhang.java.testConcurrent.Person這個類因為這個類不在jre/lib/ext目錄下或者java.ext.dirs系統(tǒng)屬性定義的目錄下祈纯,所以拋出ClassNotFoundException令宿。所以父加載器不能加載應(yīng)該被子加載器加載的類。也就是說這個類在父加載器中不可見腕窥。這種機制依賴于委派機制粒没。

下面代碼使用子加載器AppClassLoader 加載父加載器BootStrap中的類,這是可以實現(xiàn)的簇爆。

[java]view plaincopy

try{

Class.forName("java.lang.String",true,

ClassLoaderTest.class.getClassLoader());

System.out.println("2?--?類被加載");

}catch(ClassNotFoundException?e)?{

//e.printStackTrace();

System.out.println("2?--?未找到類");

}

輸出為:2 -- 類被加載癞松。說明成功加載了String類。是因為在指定由AppClassLoader加載String類時入蛆,由AppClassLoader一直委派到BootStrap加載响蓉。雖然是由子加載器的父加載器加載的,但是也可以說哨毁,父加載器加載的類對于子加載器來說是可見的枫甲。這同樣依賴于委派機制。其實在虛擬機啟動初期扼褪,java.lang.String已經(jīng)被BootStrap預(yù)加載了想幻,這時再次加載,虛擬機發(fā)現(xiàn)已經(jīng)加載迎捺,不會再重復(fù)加載举畸。這同時也證明了類加載器的單一性。

測試代碼

到此為止凳枝,類加載器的知識就全部講完了。以下是整個測試代碼:

[java]view plaincopy

packagejg.zhang.java.testclassloader;

/**

*?參考ImportNew上的一篇文章<<類加載器的工作原理>>,

*?文章地址:http://www.importnew.com/6581.html

*

*?Java類加載器基于三個機制:委托跋核、可見性和單一性岖瑰。

*?委托機制是指將加載一個類的請求交給父類加載器,如果這個父類加載器不能夠找到或者加載這個類砂代,那么再加載它蹋订。

*?可見性的原理是子類的加載器可以看見所有的父類加載器加載的類,而父類加載器看不到子類加載器加載的類刻伊。

*?單一性原理是指僅加載一個類一次露戒,這是由委托機制確保子類加載器不會再次加載父類加載器加載過的類椒功。

*

*?三種類加載器:?每個類加載器在創(chuàng)建的時候已經(jīng)指定他們對應(yīng)的目錄,?也就是說每個類加載器去哪里加載類是確定的

*?我認(rèn)為在ClassLoader類中應(yīng)該會有g(shù)etTargetPath()之類的方法,?得到他們對應(yīng)的路徑,找了找jdk的文檔,發(fā)現(xiàn)是沒有的.

*?AppClassLoader??--???加載classpath指定的路徑中的類

*?ExtClassLoader??--???加載jre/lib/ext目錄下或者java.ext.dirs系統(tǒng)屬性定義的目錄下的類

*?BootStrap???????--???加載JRE/lib/rt.jar中的類

*

*

*

*?@author?zhangjg

*

*/

publicclassClassLoaderTest?{

publicstaticvoidmain(String[]?args)?{

test1();

test2();

test3();

}

/**

*?驗證線程上下文類加載器

*/

privatestaticvoidtest3()?{

/**

*?1?每個線程都會有一個上下文類加載器,由于在線程執(zhí)行時加載用到的類,默認(rèn)情況下是父線程

*?的上下文類加載器,?也就是AppClassLoader

*/

newThread(newRunnable()?{

@Override

publicvoidrun()?{

ClassLoader?threadcontextClassLosder?=?Thread.currentThread().getContextClassLoader();

System.out.println(threadcontextClassLosder);//sun.misc.Launcher$AppClassLoader@19821f

}

}).start();

/**

*?2?也可以給創(chuàng)建的線程設(shè)定特定的上下文類加載器

*/

Thread?th?=newThread(newRunnable()?{

@Override

publicvoidrun()?{

ClassLoader?threadcontextClassLosder?=?Thread.currentThread().getContextClassLoader();

System.out.println(threadcontextClassLosder);//jg.zhang.java.testclassloader.ClassLoaderTest$3@1b67f74

}

});

th.setContextClassLoader(newClassLoader()?{});

th.start();

}

/**

*?測試可見性,可見性依賴于委托機制

*/

privatestaticvoidtest2()?{

/**

*?1?讓ExtClassLoader加載?jg.zhang.java.testConcurrent.Person這個類

*?因為這個類不在jre/lib/ext目錄下或者java.ext.dirs系統(tǒng)屬性定義的目錄下

*?所以拋出ClassNotFoundException

*

*?所以父加載器不能加載應(yīng)該被子加載器加載的類,這個類在父加載器中不可見

*?這種機制依賴于委派機制

*/

try{

Class.forName("jg.zhang.java.testConcurrent.Person",true,

ClassLoaderTest.class.getClassLoader().getParent());

System.out.println("1?--?類被加載");

}catch(ClassNotFoundException?e)?{

//e.printStackTrace();

System.out.println("1?--?未找到類");

}

/**

*?2?讓AppClassLoader加載java.lang.String類

*?沒有拋出異常,說明類被正常加載了

*?雖然是由AppClassLoader一直委派到BootStrap而加載的

*?所以可以說,父加載器加載的類對于子加載器來說是可見的,這同樣依賴于委派機制

*

*?其實在虛擬機啟動初期,java.lang.String已經(jīng)被BootStrap預(yù)加載了

*?這時再次加載,虛擬機發(fā)現(xiàn)已經(jīng)加載,不會再重復(fù)加載

*/

try{

Class.forName("java.lang.String",true,

ClassLoaderTest.class.getClassLoader());

System.out.println("2?--?類被加載");

}catch(ClassNotFoundException?e)?{

//e.printStackTrace();

System.out.println("2?--?未找到類");

}

}

/**

*?驗證三種類加載器的父子關(guān)系

*/

privatestaticvoidtest1()?{

ClassLoader?appClassLoader?=?ClassLoaderTest.class.getClassLoader();

System.out.println(appClassLoader);//sun.misc.Launcher$AppClassLoader@19821f

ClassLoader?sysClassLoader?=?ClassLoader.getSystemClassLoader();

System.out.println(sysClassLoader);//sun.misc.Launcher$AppClassLoader@19821f

//由上面的驗證可知,?應(yīng)用程序類加載器和系統(tǒng)類加載器是相同的,?因為地址是一樣的

ClassLoader?extClassLoader?=?appClassLoader.getParent();

System.out.println(extClassLoader);//sun.misc.Launcher$ExtClassLoader@addbf1

//AppClassLoader的父加載器是ExtClassLoader

System.out.println(extClassLoader.getParent());//null

//ExtClassLoader的父加載器是null,?也就是BootStrap,這是由c語言實現(xiàn)的

}

}

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市智什,隨后出現(xiàn)的幾起案子动漾,更是在濱河造成了極大的恐慌,老刑警劉巖荠锭,帶你破解...
    沈念sama閱讀 217,277評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件旱眯,死亡現(xiàn)場離奇詭異,居然都是意外死亡证九,警方通過查閱死者的電腦和手機删豺,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,689評論 3 393
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來愧怜,“玉大人呀页,你說我怎么就攤上這事∮堤常” “怎么了蓬蝶?”我有些...
    開封第一講書人閱讀 163,624評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長渴逻。 經(jīng)常有香客問我疾党,道長,這世上最難降的妖魔是什么惨奕? 我笑而不...
    開封第一講書人閱讀 58,356評論 1 293
  • 正文 為了忘掉前任雪位,我火速辦了婚禮,結(jié)果婚禮上梨撞,老公的妹妹穿的比我還像新娘雹洗。我一直安慰自己,他們只是感情好卧波,可當(dāng)我...
    茶點故事閱讀 67,402評論 6 392
  • 文/花漫 我一把揭開白布时肿。 她就那樣靜靜地躺著,像睡著了一般港粱。 火紅的嫁衣襯著肌膚如雪螃成。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,292評論 1 301
  • 那天查坪,我揣著相機與錄音寸宏,去河邊找鬼。 笑死偿曙,一個胖子當(dāng)著我的面吹牛氮凝,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播望忆,決...
    沈念sama閱讀 40,135評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼罩阵,長吁一口氣:“原來是場噩夢啊……” “哼竿秆!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起稿壁,我...
    開封第一講書人閱讀 38,992評論 0 275
  • 序言:老撾萬榮一對情侶失蹤幽钢,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后常摧,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體搅吁,經(jīng)...
    沈念sama閱讀 45,429評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,636評論 3 334
  • 正文 我和宋清朗相戀三年落午,在試婚紗的時候發(fā)現(xiàn)自己被綠了谎懦。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,785評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡溃斋,死狀恐怖界拦,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情梗劫,我是刑警寧澤享甸,帶...
    沈念sama閱讀 35,492評論 5 345
  • 正文 年R本政府宣布,位于F島的核電站梳侨,受9級特大地震影響蛉威,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜走哺,卻給世界環(huán)境...
    茶點故事閱讀 41,092評論 3 328
  • 文/蒙蒙 一蚯嫌、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧丙躏,春花似錦择示、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,723評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至废恋,卻和暖如春谈秫,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背鱼鼓。 一陣腳步聲響...
    開封第一講書人閱讀 32,858評論 1 269
  • 我被黑心中介騙來泰國打工孝常, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人蚓哩。 一個月前我還...
    沈念sama閱讀 47,891評論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像上渴,于是被迫代替她去往敵國和親岸梨。 傳聞我的和親對象是個殘疾皇子喜颁,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,713評論 2 354

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