提到類加載的概念,很多朋友可能會(huì)問(wèn),什么是類加載?類加載了解后對(duì)我們的測(cè)試開(kāi)發(fā)工作有什么幫助?在此,我們想先集中整理第一個(gè)問(wèn)題,第二個(gè)問(wèn)題只能慢慢體會(huì)。
什么是類加載?
首先,是種機(jī)制!代碼要運(yùn)行須得先經(jīng)過(guò)編譯,編譯后在工程目錄中會(huì)產(chǎn)生一堆*.class文件(這種文件是二進(jìn)制流文件);而當(dāng)JVM把這些*.class文件(里面包含類的描述數(shù)據(jù),這個(gè)描述數(shù)據(jù)暫且可理解為JVM識(shí)別的一種約定規(guī)范)加載到內(nèi)存中,并且伴隨對(duì)數(shù)據(jù)進(jìn)行校驗(yàn)、轉(zhuǎn)換解析、初始化,最終成為被JVM直接使用的Java類型,這個(gè)過(guò)程叫Java的類記載機(jī)制。
類加載包含哪些過(guò)程?
1、類的加載(此類非彼類),包含3環(huán)節(jié):
? a. 根據(jù)類名或者包名來(lái)獲取二進(jìn)制流文件;
? b. 將字節(jié)流文件所代表的靜態(tài)數(shù)據(jù)結(jié)構(gòu)轉(zhuǎn)化為方法區(qū)的運(yùn)行時(shí)的數(shù)據(jù)結(jié)構(gòu);此處只是做了數(shù) 據(jù)結(jié)構(gòu)的轉(zhuǎn)化,不含有數(shù)據(jù)的合并;(如何理解什么是方法區(qū)的運(yùn)行時(shí)的數(shù)據(jù)結(jié)構(gòu)? 方法區(qū)就是用來(lái)存放已被加載的類信息,常量,靜態(tài)變量,編譯后的代碼的運(yùn)行時(shí)內(nèi)存區(qū)域);
? c. 在內(nèi)存中生成一個(gè)代表這個(gè)類的java.lang.Class對(duì)象,作為方法區(qū)這個(gè)類的各種數(shù)據(jù)的訪問(wèn)入口;這個(gè)對(duì)象不存在于堆內(nèi)存中,而是存在于方法區(qū)中;
2、類的連接
? ? 連接階段負(fù)責(zé)將類的二進(jìn)制數(shù)據(jù)合并入JRE(Java運(yùn)行時(shí)環(huán)境)中。也包含3個(gè)環(huán)節(jié):
? ? a. 驗(yàn)證被加載后的類是否有正確的結(jié)構(gòu)
? ? b. 準(zhǔn)備:給靜態(tài)變量分配內(nèi)存和賦初值
? ? c. 解析:二進(jìn)制數(shù)據(jù)中的符號(hào)引用換為直接引用。
3、類的初始化 (真正執(zhí)行Java代碼的階段)
? ? 類的初始化的主要工作是為靜態(tài)變量賦程序設(shè)定的初值。

類加載器
在上文中講到的第一個(gè)過(guò)程中有一個(gè)重要的代碼塊不得不提,就是“類加載器”;當(dāng)然類加載器的重要作用不單單只是負(fù)責(zé)實(shí)現(xiàn)類的加載,它還與類的“相等”判定有關(guān),關(guān)系著Java“相等”判定方法的返回結(jié)果,只有在滿足如下三個(gè)“相等”判定條件,才能判定兩個(gè)類相等。
1、兩個(gè)類來(lái)自同一個(gè)Class文件
2、兩個(gè)類是由同一個(gè)虛擬機(jī)加載
3、兩個(gè)類是由同一個(gè)類加載器加載
Java“相等”判定相關(guān)方法:
1、判斷兩個(gè)實(shí)例對(duì)象的引用是否指向內(nèi)存中同一個(gè)實(shí)例對(duì)象,使用 Class對(duì)象的equals()方法,obj1.equals(obj2);
2、判斷實(shí)例對(duì)象是否為某個(gè)類、接口或其子類、子接口的實(shí)例對(duì)象,使用Class對(duì)象的isInstance()方法,class.isInstance(obj);
3、判斷實(shí)例對(duì)象是否為某個(gè)類、接口的實(shí)例,使用instanceof關(guān)鍵字,obj instanceof class;
4、判斷一個(gè)類是否為另一個(gè)類本身或其子類、子接口,可以使用Class對(duì)象的isAssignableFrom()方法,class1.isAssignableFrom(class
JVM類加載器分類詳解:
1、Bootstrap ClassLoader:?jiǎn)?dòng)類加載器,也叫根類加載器,它負(fù)責(zé)加載Java的核心類庫(kù),它不是java.lang.ClassLoader的子類,它是JVM自身內(nèi)部由C/C++實(shí)現(xiàn)的,并不是Java實(shí)現(xiàn)的
2、Extension ClassLoader:擴(kuò)展類加載器,負(fù)責(zé)核心類以外的新功能擴(kuò)展類的加載
3、System ClassLoader:系統(tǒng)類加載器或稱為應(yīng)用程序類加載器,是加載CLASSPATH環(huán)境變量所指定的jar包與類路徑
4、APP ClassLoader:用戶自定義的類加載。

類加載器的雙親委派加載機(jī)制:

加載機(jī)制主要體現(xiàn)在ClassLoader的loadClass()方法中,思路很簡(jiǎn)單:先檢查是否已經(jīng)被加載過(guò),若沒(méi)有加載則調(diào)用父類加載器的loadClass()方法,若父類加載器為空則默認(rèn)使用啟動(dòng)類加載器作為父類加載器。如果父類加載器加載失敗,拋出ClassNotFoundException異常后,調(diào)用自己的findClass()方法進(jìn)行加載。

說(shuō)明:System類與List(ArrayList繼承自該類)類都屬于Java核心類,由啟動(dòng)類加載器加載,而啟動(dòng)類加載器是在JVM內(nèi)部通過(guò)C/C++實(shí)現(xiàn)的,并不是Java,自然也就不能繼承ClassLoader類,自然就不能輸出其名稱了。
最后一個(gè)問(wèn)題:
1、雙親委派加載機(jī)制的命名由來(lái),為什么不叫“單親委派記載機(jī)制”?
2、委托機(jī)制的意義 ( 防止內(nèi)存中出現(xiàn)多份同樣的字節(jié)碼)
比如兩個(gè)類A和類B都要加載System類:如果不用委托而是自己加載自己的,那么類A就會(huì)加載一份System字節(jié)碼,然后類B又會(huì)加載一份System字節(jié)碼,這樣內(nèi)存中就出現(xiàn)了兩份System字節(jié)碼。如果使用委托機(jī)制,會(huì)遞歸的向父類查找,也就是首選用Bootstrap嘗試加載,如果找不到再向下。這里的System就能在Bootstrap中找到然后加載,如果此時(shí)類B也要加載System,也從Bootstrap開(kāi)始,此時(shí)Bootstrap發(fā)現(xiàn)已經(jīng)加載過(guò)了System那么直接返回內(nèi)存中的System即可而不需要重新加載,這樣內(nèi)存中就只有一份System的字節(jié)碼了。
3、能不能自己寫個(gè)類叫java.lang.System?
通常不可以,但可以采取另類方法達(dá)到這個(gè)需求。為了不讓我們寫System類,類加載采用委托機(jī)制,這樣可以保證爸爸們優(yōu)先,爸爸們能找到的類,兒子就沒(méi)有機(jī)會(huì)加載。而System類是Bootstrap加載器加載的,就算自己重寫,也總是使用Java系統(tǒng)提供的System,自己寫的System類根本沒(méi)有機(jī)會(huì)得到加載。但是,我們可以自己定義一個(gè)類加載器來(lái)達(dá)到這個(gè)目的,為了避免雙親委托機(jī)制,這個(gè)類加載器也必須是特殊的。由于系統(tǒng)自帶的三個(gè)類加載器都加載特定目錄下的類,如果我們自己的類加載器放在一個(gè)特殊的目錄,那么系統(tǒng)的加載器就無(wú)法加載,也就是最終還是由我們自己的加載器加載。
4、如何自定義類記載器,代碼該怎么寫?


5、類加載器有些什么樣的應(yīng)用場(chǎng)景?為什么要使用類加載器?
資源隔離 熱部署 代碼保護(hù)
Java語(yǔ)言里,類加載都是在程序運(yùn)行期間完成的,這種策略雖然會(huì)令類加載時(shí)稍微增加一些性能開(kāi)銷,但是會(huì)給java應(yīng)用程序提供高度的靈活性。例如:
a. 編寫一個(gè)面向接口的應(yīng)用程序,可能等到運(yùn)行時(shí)再指定其實(shí)現(xiàn)的子類;
b. 用戶可以自定義一個(gè)類加載器,讓程序在運(yùn)行時(shí)從網(wǎng)絡(luò)或其他地方加載一個(gè)二進(jìn)制流作為程序代碼的一部分;(這個(gè)是Android插件化,動(dòng)態(tài)安裝更新apk的基礎(chǔ))
6、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ì)象 。