1.加載蜒程,將二進制字節(jié)流加載到方法區(qū),然后在java堆中實例化一個java.lang.Class類的對象
2.驗證:文件鸳粉, 驗證class文件格式規(guī)范-魔數(shù)断医、主次版本黔衡、文件編碼廷雅;元數(shù)據(jù)耗美,對字節(jié)碼描述的信息進行語義分析:類父類、final航缀、抽象實現(xiàn)商架,字節(jié)碼,進行數(shù)據(jù)流和控制流分析:類的方法芥玉、類型轉(zhuǎn)換蛇摸,符號引用-是否存在指定類、權(quán)限
3.準備:類變量(static變量)賦0值(注:不是指定值)飞傀,final變量直接賦指定值
4.解析:符號引用轉(zhuǎn)為直接引用皇型,主要針對類或接口、字段砸烦、類方法弃鸦、接口方法四類符號引用進行
5.初始化:遇到new、getstatic幢痘、putstatic或invokestatic這4條字節(jié)碼指令時唬格,反射時如果類沒有進行過初始化,則需先觸發(fā)其初始化颜说,包括對靜態(tài)變量賦指定值购岗、靜態(tài)代碼塊的初始化,1.按代碼順序初始化门粪,2.父類先于子類初始化喊积,3.clinit ()方法不是必須的4.接口也有clint玄妈,5.線程安全
驗證-準備-解析稱為連接階段
init是instance實例構(gòu)造器乾吻,對非靜態(tài)變量解析初始化髓梅,而clinit是class類構(gòu)造器對靜態(tài)變量,靜態(tài)代碼塊進行初始化绎签。
<clinit>()方法是由編譯器自動收集類中的所有類變量的賦值動作和靜態(tài)語句塊static{}中的語句合并產(chǎn)生的
對于這五種會觸發(fā)類進行初始化的場景枯饿,這五種場景中的行為稱為對一個類進行 主動引用。除此之外诡必,所有引用類的方式奢方,都不會觸發(fā)初始化,稱為 被動引用爸舒。
類的實例化是指創(chuàng)建一個類的實例(對象)的過程蟋字;
類的初始化是指為類中各個類成員(被static修飾的成員變量)賦初始值的過程,是類生命周期中的一個階段碳抄。
通過子類引用父類的靜態(tài)字段愉老,不會導致子類初始化:例子
實例初始化不一定要在類初始化結(jié)束之后才開始初始化。例子
創(chuàng)建一個對象常常需要經(jīng)歷如下幾個過程:父類的類構(gòu)造器<clinit>() -> 子類的類構(gòu)造器<clinit>() -> 父類的成員變量和實例代碼塊 -> 父類的構(gòu)造函數(shù) -> 子類的成員變量和實例代碼塊 -> 子類的構(gòu)造函數(shù)剖效。
淺克录等搿:復制的對象時不同的對象,對象內(nèi)部的基本數(shù)據(jù)克隆正常璧尸,但是引用類型不會克隆咒林,只會指向同一個
類加載的過程(加載、驗證爷光、準備垫竞、解析、初始化)
JVM(三):類加載機制(類加載過程和類加載器)
深入理解Java類加載器
1.加載
“加載”是”類加載”過程的一個階段蛀序。由類加載器完成欢瞪,在加載階段,虛擬機需要完成以下3件事情:
1.通過一個類的全限定名來獲取定義此類的二進制字節(jié)流徐裸。(并沒有指明要從一個Class文件中獲取遣鼓,可以從其他渠道,譬如:網(wǎng)絡(luò)重贺、動態(tài)生成骑祟、數(shù)據(jù)庫等);
2.將這個字節(jié)流所代表的靜態(tài)存儲結(jié)構(gòu)轉(zhuǎn)化為方法區(qū)的運行時數(shù)據(jù)結(jié)構(gòu)气笙。
3.在內(nèi)存中生成一個代表這個類的java.lang.Class對象次企,作為方法區(qū)這個類的各種數(shù)據(jù)的訪問入口。
相對于類加載過程的其他階段潜圃,加載階段(準備地說缸棵,是加載階段中獲取類的二進制字節(jié)流的動作)是開發(fā)期可控性最強的階段,因為加載階段可以使用系統(tǒng)提供的類加載器(ClassLoader)來完成谭期,也可以由用戶自定義的類加載器完成堵第,開發(fā)人員可以通過定義自己的類加載器去控制字節(jié)流的獲取方式稚晚。
加載階段和連接階段(Linking)的部分內(nèi)容(如一部分字節(jié)碼文件格式驗證動作)是交叉進行的,加載階段尚未完成型诚,連接階段可能已經(jīng)開始,
但這些夾在加載階段之中進行的動作鸳劳,仍然屬于連接階段的內(nèi)容狰贯,這兩個階段的開始時間仍然保持著固定的先后順序。
2.驗證:
文件驗證:魔數(shù)開頭赏廓、主次版本涵紊、文件編碼
元數(shù)據(jù)驗證:類相關(guān)驗證、父類幔摸、抽象方法摸柄、方法重載出錯
字節(jié)碼驗證:對類的方法體驗證、類型轉(zhuǎn)換
符號引用驗證:對類引用的常量池中的各種符號引用檢查能否找到既忆、權(quán)限驱负、public、private等
驗證是連接階段的第一步患雇,這一階段的目的是為了確保Class文件的字節(jié)流中包含的信息符合當前虛擬機的要求跃脊,并不會危害虛擬機的自身安全。
驗證階段是非常重要的苛吱,但不是必須的酪术,它對程序運行期沒有影響。
1.文件格式驗證:
驗證class文件格式規(guī)范翠储,
(1)是否以魔數(shù)0xCAFEBABE開頭绘雁。
(2)主、次版本號是否在當前虛擬機處理范圍之內(nèi)援所。
(3)常量池的常量中是否有不被支持的常量類型(檢查常量tag標志)庐舟。
(4)指向常量的各種索引值中是否有指向不存在的常量或不符合類型的常量。
(5)CONSTANT_Utf8_info型的常量中是否有不符合UTF8編碼的數(shù)據(jù)任斋。
(6)Class文件中各個部分及文件本身是否有被刪除的或附加的其他信息继阻。
......
2.元數(shù)據(jù)驗證:
這個階段是對字節(jié)碼描述的信息進行語義分析,以保證起描述的信息符合java語言規(guī)范要求废酷。
(1)這個類是否有父類(除了java.lang.Object之外瘟檩,所有類都應當有父類)。
(2)這個類是否繼承了不允許被繼承的類(被final修飾的類)澈蟆。
(3)如果這個類不是抽象類墨辛,是否實現(xiàn)了其父類或接口之中所要求實現(xiàn)的所有方法。
(4)類中的字段趴俘、方法是否與父類產(chǎn)生矛盾(例如覆蓋了父類的final字段睹簇,或者出現(xiàn)不符合規(guī)則的方法重載奏赘,例如方法參數(shù)都一致,但返回值類型卻不同等等)太惠。
......
3.字節(jié)碼驗證:
進行數(shù)據(jù)流和控制流分析磨淌,這個階段對類的方法體進行校驗分析,確定程序語義是合法的凿渊、符合邏輯的梁只。保證被校驗類的方法在運行時不會產(chǎn)生危害虛擬機安全的事件,例如:
(1)保證任意時刻操作數(shù)棧的數(shù)據(jù)類型與指令代碼序列都能配合工作埃脏,例如不會出現(xiàn)類似這樣的情況:在操作數(shù)棧放置了一個int類型的數(shù)據(jù)搪锣,使用時卻按long類型來加載入本地變量表中。
(2)保證跳轉(zhuǎn)指令不會跳轉(zhuǎn)到方法體以外的字節(jié)碼指令上彩掐。
(3)保證方法體中的類型轉(zhuǎn)換是有效的构舟,例如可以把一個子類對象賦值給父類數(shù)據(jù)類型,但是把父類對象賦值給子類數(shù)據(jù)類型堵幽,甚至把對象賦值給與它毫無繼承關(guān)系狗超、完全不相干的一個數(shù)據(jù)類型,則是危險不合法的朴下。
......
(Halting Problem:通過程序去校驗程序邏輯是無法做到絕對準確的——不能通過程序準確的檢查出程序是否能在有限時間之內(nèi)結(jié)束運行抡谐。)
4.符號引用驗證:
符號引用中通過字符串描述的全限定名是否能(在常量池中)找到對應的類,符號引用類中的類桐猬,字段和方法的訪問性(private麦撵、protected、public溃肪、default)是否可被當前類訪問免胃。
符號引用驗證可以看作是類對自身以外(常量池中的各種符號引用)的信息進行匹配性校驗,通常需要校驗以下內(nèi)容:
(1)符號引用中通過字符串描述的全限定名是否能夠找到對應的類惫撰。
(2)在指定類中是否存在符合方法的字段描述符以及簡單名稱所描述的方法和字段羔沙。
(3)符號引用中的類、字段厨钻、方法的訪問性(private扼雏、protected、public夯膀、default)是否可被當前類訪問诗充。
最后一個階段的校驗發(fā)生在虛擬機將符號引用轉(zhuǎn)化為直接引用的時候,這個轉(zhuǎn)化動作將在連接的第三個階段——解析階段中發(fā)生诱建。符號引用驗證可以看做是對類自身以外的信息進行匹配性的校驗蝴蜓,通常需要校驗以下內(nèi)容:
符號引用中通過字符串描述的全限定名是否能找到對應的類。
在指定類中是否存在符合方法的字段描述符以及簡單名稱所描述的方法和字段。
符號引用中的類茎匠、字段和方法的訪問性(private格仲、protected、public诵冒、default)是否可被當前類訪問凯肋。
。汽馋。否过。。惭蟋。。
符號引用驗證的目的是確保解析動作能正常執(zhí)行药磺,如果無法通過符號引用驗證告组,將會拋出一個java.lang.IncompatibleClassChangeError異常的子類,如java.lang.IllegalAccessError癌佩、java.lang.NoSuchFieldError木缝、java.lang.NoSuchMethodError等。
驗證階段對于虛擬機的類加載機制來說围辙,是一個非常重要我碟、但不一定是必要的階段。如果所運行的全部代碼都已經(jīng)被反復使用和驗證過姚建,在實施階段就可以考慮使用-Xverify:none參數(shù)來關(guān)閉大部分的類驗證措施
3. 準備
準備階段是正式為類變量分配內(nèi)存并設(shè)置類變量初始值(\零值)的階段矫俺,這些變量所使用的內(nèi)存都將在方法區(qū)中進行分配。這時候進行內(nèi)存分配的僅包括類變量(被static修飾的變量)掸冤,而不包括實例變量厘托,實例變量將會在對象實例化的時候隨著對象一起分配在Java堆中。
如果類字段的字段屬性表中存在ConstantValue(不變)屬性稿湿,那在準備階段變量value就會被初始化為ConstantValue屬性所指定的值铅匹,假設(shè)類變量value定義為:
public static final int value = 123;
編譯時Javac將會為value生成ConstantValue屬性,在準備階段虛擬機就會根據(jù)ConstantValue的設(shè)置將value賦值為123.
4.解析
解析階段是虛擬機將常量池內(nèi)的符號引用替換為直接引用的過程饺藤。
符號引用(Symbolic References):符號引用以一組符號來描述所引用的目標包斑,符號可以是任何形式的字面量,只要使用時能無歧義地定位到目標即可涕俗。符號引用與虛擬機實現(xiàn)的內(nèi)存布局無關(guān)罗丰,引用的目標對象并不一定已經(jīng)加載到內(nèi)存中。
直接引用(Direct References):直接引用可以是直接指向目標的指針再姑、相對偏移量或是一個能間接定位到目標的句柄丸卷。如果有了直接引用,那么引用的目標一定是已經(jīng)存在于內(nèi)存中询刹。
虛擬機規(guī)范并沒有規(guī)定解析階段發(fā)生的具體時間,所以虛擬機實現(xiàn)會根據(jù)需要來判斷谜嫉,到底是在類被加載器加載時就對常量池中的符號引用進行解析萎坷,還是等到一個符號引用將要被使用前才去解析它。
解析的動作主要針對類或接口沐兰、字段哆档、類方法、接口方法四類符號引用進行住闯。分別對應編譯后常量池內(nèi)的CONSTANT_Class_Info瓜浸、CONSTANT_Fieldref_Info、CONSTANT_Methodef_Info比原、CONSTANT_InterfaceMethoder_Info四種常量類型插佛。
1.類、接口的解析
2.字段解析
3.類方法解析
4.接口方法解析
1.類或接口的解析
假設(shè)當前代碼所處的類為D量窘,如果要把一個從未解析過的符號引用N解析為一個類或接口C的引用雇寇,那虛擬機完成整個解析過程需要以下3個步驟:
(1)如果C不是一個數(shù)組類型,那虛擬機將會把代表N的全限定名傳遞給D的類加載器去加載這個類C蚌铜。
(2)如果C是一個數(shù)組類型锨侯,并且數(shù)組的元素類型為對象,那將會按照第1點的規(guī)則加載數(shù)組元素類型冬殃。
(3)如果上面的步驟沒有出現(xiàn)任何異常囚痴,那么C在虛擬機中實際上已經(jīng)成為了一個有效的類或接口了,但在解析完成之前還要進行符號引用驗證审葬,確認D是否具有對C的訪問權(quán)限深滚。如果發(fā)現(xiàn)不具備訪問權(quán)限,則拋出java.lang.IllegalAccessError
異常涣觉。
2.字段解析
首先解析字段表內(nèi)class_index項中索引的CONSTANT_Class_info符號引用成箫,也就是字段所屬的類或接口的符號引用,如果解析完成旨枯,將這個字段所屬的類或接口用C表示蹬昌,虛擬機規(guī)范要求按照如下步驟對C進行后續(xù)字段的搜索。
(1)如果C 本身就包含了簡單名稱和字段描述符都與目標相匹配的字段攀隔,則返回這個字段的直接引用皂贩,查找結(jié)束。
(2)否則昆汹,如果C中實現(xiàn)了接口明刷,將會按照繼承關(guān)系從下往上遞歸搜索各個接口和它的父接口如果接口中包含了簡單名稱和字段描述符都與目標相匹配的字段,則返回這個字段的直接引用满粗,查找結(jié)束辈末。
(3)否則,如果C 不是java.lang.Object的話,將會按照繼承關(guān)系從下往上遞歸搜索其父類挤聘,如果在父類中包含了簡單名稱和字段描述符都與目標相匹配的字段轰枝,則返回這個字段的直接引用,查找結(jié)束组去。
(4)否則鞍陨,查找失敗,拋出java.lang.NoSuchFieldError
異常从隆。
如果查找過程成功返回了引用诚撵,將會對這個字段進行權(quán)限驗證,如果發(fā)現(xiàn)不具備對字段的訪問權(quán)限键闺,將拋出java.lang.IllegalAccessErro
r異常寿烟。
如果有一個同名字段同時出現(xiàn)在C的接口和父類中,或者同時在自己的父類或多個接口中出現(xiàn)辛燥,那編譯器可能拒絕編譯筛武,并提示”The field xxx is ambiguous”。
3.類方法解析
首先解析類方法表內(nèi)class_index項中索引的CONSTANT_Class_info符號引用购桑,也就是方法所屬的類或接口的符號引用,如果解析完成氏淑,將這個類方法所屬的類或接口用C表示勃蜘,虛擬機規(guī)范要求按照如下步驟對C進行后續(xù)類方法的搜索。
(1)類方法和接口方法符號引用的常量類型定義是分開的假残,如果在類方法表中發(fā)現(xiàn)class_index中索引的C 是個接口缭贡,那就直接拋出java.lang.IncompatibleClassChangeError異常。
(2)如果通過了第一步辉懒,在類C 中查找是否有簡單名稱和描述符都與目標相匹配的方法阳惹,如果有則返回這個方法的直接引用,查找結(jié)束眶俩。
(3)否則莹汤,在類C的父類中遞歸查找是否有簡單名稱和描述符都與目標相匹配的方法,如果有則返回這個方法的直接引用颠印,查找結(jié)束纲岭。
(4)否則,在類C實現(xiàn)的接口列表以及他們的父接口中遞歸查找是否有簡單名稱和描述符都與目標相匹配的方法线罕,如果存在相匹配的方法止潮,說明類C是一個抽象類這時查找結(jié)束,拋出java.lang.AbstractMethodError異常钞楼。
(5)否則喇闸,宣告方法查找失敗,拋出java.lang.NoSuchMethodError。
最后燃乍,如果查找成功返回了直接引用唆樊,將會對這個方法進行權(quán)限驗證,如果發(fā)現(xiàn)不具備此方法的訪問權(quán)限橘沥,則拋出java.lang.IllegalAccessError異常窗轩。
4.接口方法解析
首先解析接口方法表內(nèi)class_index項中索引的CONSTANT_Class_info符號引用,也就是方法所屬的類或接口的符號引用座咆,如果解析完成痢艺,將這個接口方法所屬的接口用C表示,虛擬機規(guī)范要求按照如下步驟對C進行后續(xù)接口方法的搜索介陶。
(1)與類解析方法不同堤舒,如果在接口方法表中發(fā)現(xiàn)class_index中的索引C是個類而不是個接口,那就直接拋出java.lang.IncompatibleClassChangeError異常哺呜。
(2)否則舌缤,在接口C中查找是否有簡單名稱和描述符都與目標相匹配的方法,如果有則返回這個方法的直接引用某残,查找結(jié)束国撵。
(3)否則,在接口C的父接口中遞歸查找玻墅,直到j(luò)ava.lang.Object類(查找范圍包括Object類)為止介牙,看是否有簡單名稱和描述符都與目標相匹配的方法,如果有則返回這個方法的直接引用澳厢,查找結(jié)束环础。
(4)否則,宣告方法查找失敗剩拢,拋出java.lang.NoSuchMethodError线得。
由于接口中所有的方法默認都是public的,所以不存在訪問權(quán)限的問題徐伐,因此接口方法的符號解析應當不會拋出java.lang.IllegalAccessError異常贯钩。
5.初始化
類的初始化階段是類加載過程的最后一步,在準備
階段办素,類變量已賦過一次系統(tǒng)要求的初始值0值魏保,而在初始化階段,則是根據(jù)程序員通過程序制定的主觀計劃去初始化類變量和其他資源摸屠,或者可以從另外一個角度來表達:初始化階段是執(zhí)行類構(gòu)造器<clinit>()方法的過程
谓罗。
<clinit>()方法是由編譯器自動收集類中的所有類變量的賦值動作和靜態(tài)語句塊static{}中的語句合并產(chǎn)生的
在以下四種情況下初始化過程會被觸發(fā)執(zhí)行:
- 遇到new、getstatic季二、putstatic或invokestatic這4條字節(jié)碼指令時檩咱,如果類沒有進行過初始化揭措,則需先觸發(fā)其初始化。生成這4條指令的最常見的java代碼場景是:1.使用new關(guān)鍵字實例化對象刻蚯;2-3.讀取或設(shè)置一個類的靜態(tài)變量的時候(final除外绊含,被final修飾、已在編譯器把結(jié)果放入常量池)炊汹;4.調(diào)用類的靜態(tài)方法的時候躬充。
- 使用java.lang.reflect包的方法對類進行反射調(diào)用的時候
- 當初始化一個類的時候,如果發(fā)現(xiàn)其父類還沒有進行過初始化讨便、則需要先出發(fā)其父類的初始化
- jvm啟動時充甚,用戶指定一個執(zhí)行的主類(包含main方法的那個類),虛擬機會先初始化這個類
類初始化階段是類加載過程的最后一步霸褒,到了這個階段才真正開始執(zhí)行類中定義的Java程序代碼(或者說是字節(jié)碼)伴找。在準備階段,變量已經(jīng)賦過一次系統(tǒng)要求的初始值0值废菱,而在初始化階段技矮,則對類變量賦指定值。
1.編譯器收集的順序是由語句在源文件中出現(xiàn)的順序決定的殊轴,靜態(tài)語句塊中只能訪問到定義在靜態(tài)語句塊之前的變量衰倦,而定義在它之后的變量,在前面的靜態(tài)語句塊可以賦值旁理,但不能訪問樊零,代碼解釋如下:
public class Test {
static {
i = 0; //給變量賦值可以正常編譯通過
System.out.print(i); //編譯器會提示“非法向前引用”
}
static int i = 1;
}
2.初始化方法執(zhí)行的順序,虛擬機會保證在子類的初始化方法執(zhí)行之前韧拒,父類的初始化方法已經(jīng)執(zhí)行完畢,也就是說子類初始化時父類的所有靜態(tài)變量淹接、靜態(tài)代碼塊 都執(zhí)行完了十性。
3.clinit ()方法對于類或接口來說并不是必須的叛溢,如果一個類中沒有靜態(tài)語句塊,或者沒有對靜態(tài)變量的賦值操作劲适,那么編譯器可以不為這個類生成clinit()方法楷掉。
4.接口中不能使用靜態(tài)語句塊,但仍然有變量初始化的操作霞势,因此接口與類一樣都會生成clinit()方法烹植,但與類不同的是,執(zhí)行接口的初始化方法之前愕贡,不需要先執(zhí)行父接口的初始化方法草雕。只有當父接口中定義的變量使用時,才會執(zhí)行父接口的初始化方法固以。另外墩虹,接口的實現(xiàn)類在初始化時也一樣不會執(zhí)行接口的clinit()方法嘱巾。
5.虛擬機會保證一個類的clinit()方法在多線程環(huán)境中被正確的加鎖、同步诫钓,如果多個線程同時去初始化一個類旬昭,那么只會有一個線程去執(zhí)行這個類的clinit()方法,其他線程都需要阻塞等待菌湃,直到活動線程執(zhí)行類初始化方法完畢问拘。
. Java虛擬機加載.class過程
虛擬機把Class文件加載到內(nèi)存,然后進行校驗惧所,解析和初始化骤坐,最終形成java類型,這就是虛擬機的類加載機制纯路。加載或油,驗證,準備驰唬,初始化這5個階段的順序是確定的顶岸,
類的加載過程,必須按照這種順序開始叫编。這些階段通常是相互交叉和混合進行的辖佣。解析階段在某些情況下,可以在初始化階段之后再開始---為了支持java語言的運行時綁定搓逾。
Java虛擬機規(guī)范中卷谈,沒有強制約束什么時候要開始加載,但是霞篡,卻嚴格規(guī)定了幾種情況必須進行初始化(加載世蔗,驗證,準備則需要在初始化之前開始):
1)遇到 new朗兵、getstatic污淋、putstatic、或者invokestatic 這4條字節(jié)碼指令余掖,如果沒有類沒有進行過初始化寸爆,則觸發(fā)初始化
2)使用java.lang.reflect包的方法,對壘進行反射調(diào)用的時候盐欺,如果沒有初始化赁豆,則先觸發(fā)初始化
3)初始化一個類時候,如果發(fā)現(xiàn)父類沒有初始化冗美,則先觸發(fā)父類的初始化
- 加載魔种,驗證,解析
加載就是通過指定的類全限定名粉洼,獲取此類的二進制字節(jié)流节预,然后將此二進制字節(jié)流轉(zhuǎn)化為方法區(qū)的數(shù)據(jù)結(jié)構(gòu)甲抖,在內(nèi)存中生成一個代表這個類的Class對象。驗證是為了確
保Class文件中的字節(jié)流符合虛擬機的要求心铃,并且不會危害虛擬機的安全准谚。加載和驗證階段比較容易理解,這里就不再過多的解釋去扣。解析階段比較特殊柱衔,解析階段是虛擬機
將常量池中的符號引用轉(zhuǎn)換為直接引用的過程。如果想明白解析的過程愉棱,得先了解一點class文件的一些信息唆铐。class文件采用一種類似C語言的結(jié)構(gòu)體的偽結(jié)構(gòu)來存儲我們編
碼的java類的各種信息。其中奔滑,class文件中常量池(constant_pool)是一個類似表格的倉庫艾岂,里面存儲了我們編寫的java類的類和接口的全限定名,字段的名稱和描述符朋其,
方法的名稱和描述符王浴。在java虛擬機將class文件加載到虛擬機內(nèi)存之后,class類文件中的常量池信息以及其他的數(shù)據(jù)會被保存到j(luò)ava虛擬機內(nèi)存的方法區(qū)梅猿。我們知道class文件
的常量池存放的是java類的全名氓辣,接口的全名和字段名稱描述符,方法的名稱和描述符等信息袱蚓,這些數(shù)據(jù)加載到j(luò)vm內(nèi)存的方法區(qū)之后钞啸,被稱做是符號引用。而把這些類的
全限定名喇潘,方法描述符等轉(zhuǎn)化為jvm可以直接獲取的jvm內(nèi)存地址体斩,指針等的過程,就是解析颖低。虛擬機實現(xiàn)可以對第一次的解析結(jié)果進行緩存絮吵,避免解析動作的重復執(zhí)行。
在解析類的全限定名的時候枫甲,假設(shè)當前所處的類為D源武,如果要把一個從未解析過的符號引用N解析為一個類或者接口C的直接引用扼褪,具體的執(zhí)行辦法就是虛擬機會把代表N的
全限定名傳遞給D的類加載器去加載這個類C想幻。這塊可能不太好理解,但是我們可以直接理解為調(diào)用D類的ClassLoader來加載N话浇,然后就完成了N--->C的解析脏毯,就可以了。