一定要記清楚啊批销,這么基礎(chǔ)性的問題,不會的話多尷尬
- 一般來說顷蟀,我們把Java的類加載過程分為三個主要步驟:加載、鏈接鸣个、初始化,具體行為在Java虛擬機(jī)規(guī)范里有非常詳細(xì)的定義囤萤。
- 首先是加載階段(Loading),它是Java將字節(jié)碼數(shù)據(jù)從不同的數(shù)據(jù)源讀取到JVM中澄惊,并映射為JVM認(rèn)可的數(shù)據(jù)結(jié)構(gòu)(Class對象)富雅,這里的數(shù)據(jù)源可能是各種各樣的形態(tài),如jar文件没佑、class文件,甚至是網(wǎng)絡(luò)數(shù)據(jù)源等蛤奢;如果輸入數(shù)據(jù)不是ClassFile的結(jié)構(gòu),則會拋出ClassFormatError待秃。
- 加載階段是用戶參與的階段,我們可以自定義類加載器章郁,去實(shí)現(xiàn)自己的類加載過程志衍。
- 第二階段是鏈接(Linking),這是核心的步驟足画,簡單說是把原始的類定義信息平滑地轉(zhuǎn)化入JVM運(yùn)行的過程中佃牛。這里可進(jìn)一步細(xì)分為三個步驟:
- 驗(yàn)證(Verification),這是虛擬機(jī)安全的重要保障象缀,JVM需要核驗(yàn)字節(jié)信息是符合Java虛擬機(jī)規(guī)范的,否則就被認(rèn)為是VerifyError央星,這樣就防止了惡意信息或者不合規(guī)的信息危害JVM的運(yùn)行惫东,驗(yàn)證階段有可能觸發(fā)更多class的加載毙石。
- 準(zhǔn)備(Preparation)颓遏,創(chuàng)建類或接口中的靜態(tài)變量,并初始化靜態(tài)變量的初始值叁幢。但這里的“初始化”和下面的顯式初始化階段是有區(qū)別的曼玩,側(cè)重點(diǎn)在于分配所需要的內(nèi)存空間,不會去執(zhí)行更進(jìn)一步的JVM指令黍判。
- 解析(Resolution),在這一步會將常量池中的符號引用(symbolic reference)替換為直接引用样悟。在Java虛擬機(jī)規(guī)范中,詳細(xì)介紹了類陈症、接口震糖、方法和字段等各個方面的解析。
- 最后是初始化階段(initialization)吊说,這一步真正去執(zhí)行類初始化的代碼邏輯,包括靜態(tài)字段賦值的動作颁井,以及執(zhí)行類定義中的靜態(tài)初始化塊內(nèi)的邏輯,編譯器在編譯階段就會把這部分邏輯整理好养涮,父類型的初始化邏輯優(yōu)先于當(dāng)前類型的邏輯眉抬。
-
再來談?wù)勲p親委派模型,簡單說就是當(dāng)類加載器(Class-Loader)試圖加載某個類型的時候蜀变,除非父加載器找不到相應(yīng)類型,否則盡量將這個任務(wù)代理給當(dāng)前加載器的父加載器去做爬舰。使用委派模型的目的是避免重復(fù)加載Java類型们陆。
類的加載機(jī)制 - 通常類加載機(jī)制有三個基本特征:
- 雙親委派模型棒掠。但不是所有類加載都遵守這個模型屁商,有的時候烟很,啟動類加載器所加載的類型雾袱,是可能要加載用戶代碼的官还,比如JDK內(nèi)部的ServiceProvider/ServiceLoader機(jī)制,用戶可以在標(biāo)準(zhǔn)API框架上望伦,提供自己的實(shí)現(xiàn),JDK也需要提供些默認(rèn)的參考實(shí)現(xiàn)腿箩。 例如,Java 中JNDI珠移、JDBC末融、文件系統(tǒng)、Cipher等很多方面勾习,都是利用的這種機(jī)制,這種情況就不會用雙親委派模型去加載巧婶,而是利用所謂的上下文加載器。
- 可見性,子類加載器可以訪問父加載器加載的類型骂倘,但是反過來是不允許的,不然诅需,因?yàn)槿鄙俦匾母綦x,我們就沒有辦法利用類加載器去實(shí)現(xiàn)容器的邏輯堰塌。
- 單一性,由于父加載器的類型對于子加載器是可見的场刑,所以父加載器中加載過的類型,就不會在子加載器中重復(fù)加載铐懊。但是注意瞎疼,類加載器“鄰居”間,同一類型仍然可以被加載多次贼急,因?yàn)榛ハ嗖⒉豢梢姟?/li>