每天進(jìn)步一點(diǎn)點(diǎn)!
上一篇穿插了一段動(dòng)態(tài)綁定和靜態(tài)綁定的知識(shí),這一篇我們回歸到類加載上來(lái),學(xué)習(xí)一下類加載的“加載”。
是不是讀起來(lái)有點(diǎn)拗口,這是什么意思?

別迷糊,還記得上一篇的上一篇學(xué)習(xí)過(guò)的類加載過(guò)程嗎,里面有一個(gè)階段就是“加載(loading)”。
加載過(guò)程主要包括以下三點(diǎn)內(nèi)容:
1、通過(guò)一個(gè)類的全限定名來(lái)獲取定義此類的二進(jìn)制字節(jié)流。
全限定名也就是包名.類名的形式。如下圖所示,當(dāng)我們?cè)谕粋€(gè)類中,引用的兩個(gè)類名相同的類,都是StringUtils的時(shí)候,就需要使用全限定名org.apache.commons.lang.StringUtils進(jìn)行區(qū)分,另一個(gè)沒有帶包名的,則默認(rèn)使用import引入的包。

注意,這里說(shuō)的二進(jìn)制字節(jié)流,并沒有明確一定是從Class文件中獲取,這就演化出了一些其它形式,比如:壓縮文件,可以是jar包和war包;Applet可以從網(wǎng)絡(luò)中獲??;使用動(dòng)態(tài)代理技術(shù)(CGLIB等)生成的二進(jìn)制流;通過(guò)其它文件生成,如jsp文件生成的class文件。還可以從數(shù)據(jù)庫(kù)讀取,等等……
另外,還有一個(gè)問題,如果我手動(dòng)寫一個(gè)java.lang.System打成一個(gè)包加載到虛擬機(jī)中,是不是會(huì)對(duì)rt.jar包中的System產(chǎn)生影響呢?這里就要說(shuō)說(shuō)類加載器了。
java程序一般會(huì)用到三種類加載器:
1)啟動(dòng)類加載器(Bootstrap
ClassLoader):負(fù)責(zé)加載JAVA_HOME\lib或者被-Xbootclasspath參數(shù)所指定的路徑中的目錄中,并且能被虛擬機(jī)識(shí)別的類庫(kù)到JVM內(nèi)存中,如果名稱不符合的類庫(kù)即使放在lib目錄中也不會(huì)被加載。這個(gè)類加載器是虛擬器自身的一部分,無(wú)法被Java程序直接引用(HotSpot中采用C++編寫)。
2)擴(kuò)展類加載器(Extension ClassLoader):該加載器主要是負(fù)責(zé)加載JAVA_HOME\lib\ext目錄下的類,或者java.ext.dirs指定目錄下的類,該加載器可以被開發(fā)者直接使用。
java.ext.dirs可以通過(guò)-Djava.ext.dirs命令進(jìn)行設(shè)置(web開發(fā)中很少會(huì)用到),我們可以在系統(tǒng)屬性中取到。

運(yùn)行結(jié)果如下。

3)應(yīng)用程序類加載器(Application
ClassLoader):該類加載器也稱為應(yīng)用程序類加載器,它負(fù)責(zé)加載用戶類路徑(Classpath)上所指定的類庫(kù),開發(fā)者可以直接使用該類加載器,如果應(yīng)用程序中沒有自定義過(guò)自己的類加載器,一般情況下這個(gè)就是程序中默認(rèn)的類加載器。

既然知道了實(shí)現(xiàn)類是什么,我們不妨打開類文件看一眼。


看起來(lái)是不是更直觀了,我們?cè)偕葾ppClassLoader的類圖簡(jiǎn)單看一下,對(duì)該類的繼承關(guān)系有一個(gè)大概的了解。

在虛擬機(jī)實(shí)際的工作當(dāng)中,類加載器之間是存在層次關(guān)系的,如下圖所示,

這種包含優(yōu)先級(jí)的層次關(guān)系被稱為雙親委派模型,即如果一個(gè)類加載器收到了一個(gè)類加載請(qǐng)求,它不會(huì)自己去嘗試加載這個(gè)類,而是把這個(gè)請(qǐng)求轉(zhuǎn)交給父類加載器去完成。每一個(gè)層次的類加載器都是如此。因此所有的類加載請(qǐng)求都應(yīng)該傳遞到最頂層的啟動(dòng)類加載器中,只有到父類加載器反饋?zhàn)约簾o(wú)法完成這個(gè)加載請(qǐng)求(在它的搜索范圍沒有找到這個(gè)類)時(shí),子類加載器才會(huì)嘗試自己去加載。
那么也就是說(shuō)當(dāng)虛擬機(jī)發(fā)現(xiàn)需要加載java.lang.System類的時(shí)候,會(huì)最先使用啟動(dòng)類加載器去尋找,避免了類被重復(fù)加載。
2、將這個(gè)字節(jié)流所代表的靜態(tài)存儲(chǔ)結(jié)構(gòu)轉(zhuǎn)化為方法區(qū)的運(yùn)行時(shí)數(shù)據(jù)結(jié)構(gòu)。
還記得我們最開始學(xué)習(xí)的虛擬機(jī)管理的數(shù)據(jù)區(qū)域的內(nèi)容嗎,虛擬機(jī)加載的類信息,常量,靜態(tài)變量,即時(shí)編譯器編譯的代碼等數(shù)據(jù)存儲(chǔ)在方法區(qū)。
3、在內(nèi)存中生成一個(gè)代表這個(gè)類的java.lang.Class對(duì)象,作為方法區(qū)這個(gè)類的各種數(shù)據(jù)的訪問入口。
Class對(duì)象是一個(gè)特殊的對(duì)象,是用來(lái)創(chuàng)建其它對(duì)象的對(duì)象,經(jīng)常會(huì)用到的如Class.forName("className")方法,如下圖所示,

這個(gè)方法運(yùn)行后出現(xiàn)一個(gè)有意思的結(jié)果,第一個(gè)輸出為true,第二個(gè)為false。
因?yàn)樽鳛閰⒄盏腟ameClassNameTest是由應(yīng)用程序加載器加載的,而obj2是使用自定義的類加載器ClassLoader加載后創(chuàng)建的,所以并不相等,是不是很神奇?
喜歡文章或想一起學(xué)習(xí)的朋友可以關(guān)注我,給我點(diǎn)贊,我將會(huì)持續(xù)更新,有什么疑問或文中有不當(dāng)之處請(qǐng)給我留言,真誠(chéng)地希望能與大家一起交流探討,學(xué)習(xí)進(jìn)步。