虛擬機(jī)類加載機(jī)制

定義

所謂的類加載就是將class文件讀入內(nèi)存,校驗(yàn)、解析和初始化,使其成為可以被Java虛擬機(jī)直接使用的Java類型。類的加載機(jī)制核心階段有三個(gè):加載、鏈接、初始化,其中鏈接階段又細(xì)分為驗(yàn)證、準(zhǔn)備、解析,所以類加載也可分為5個(gè)階段分別為:加載、驗(yàn)證、準(zhǔn)備、解析、初始化,下面在詳細(xì)講解這些階段

加載階段

類加載階段分三步:

1. 根據(jù)類的完全限定名獲取類的二進(jìn)制字節(jié)流

2. 根據(jù)字節(jié)流將類的靜態(tài)結(jié)構(gòu)轉(zhuǎn)化為方法區(qū)的運(yùn)行時(shí)結(jié)構(gòu)

3. 在內(nèi)存中生一個(gè)代表這個(gè)類的Class對(duì)象,作為方法區(qū)這個(gè)類的入口(通過(guò)這個(gè)class對(duì)象訪問(wèn)第二步的運(yùn)行時(shí)結(jié)構(gòu))。雖然Class對(duì)象是對(duì)象類型,但在HotSpot虛擬機(jī)中,Class對(duì)象并沒(méi)有放在java堆而放在了方法區(qū)

注意:一個(gè)類必須與類加載器一起確定唯一性,而每一個(gè)類加載器都擁有一個(gè)獨(dú)立的類名稱空間

數(shù)組加載階段,與類加載階段有所不同,數(shù)組加載先根據(jù)數(shù)組類的元素類型進(jìn)行類型加載,如果元素類型是引用類型則先加載類,加載步驟與上面的類加載階段相同并把數(shù)組標(biāo)識(shí)在該類加載器的命名空間中,如果元素類型不是引用類型(如int [])則該數(shù)組則由引導(dǎo)類加載器關(guān)聯(lián),而數(shù)組類本身則由Java虛擬機(jī)直接創(chuàng)建。

鏈接階段

驗(yàn)證階段

檢查Class文件是否符合Java虛擬機(jī)規(guī)范,防止破壞Java虛擬機(jī),這個(gè)階段包括:文件格式驗(yàn)證、元數(shù)據(jù)驗(yàn)證、字節(jié)碼驗(yàn)證

準(zhǔn)備階段

為類的靜態(tài)變量分配內(nèi)存并賦予默認(rèn)值,如果該變量被修飾為final則馬上根據(jù)設(shè)置值復(fù)制,如static final int a=123;既a的值在準(zhǔn)備階段直接賦為123

解析階段

虛擬機(jī)將常量池內(nèi)的符號(hào)引用替換為直接引用,轉(zhuǎn)換過(guò)程中如果該符號(hào)引用代表的類未加載則加載該類。

解析階段的解析類型分別有:類或接口解析、字段解析、類方法解析、接口方法的解析

初始化階段

初始化本質(zhì)上是<client>方法的執(zhí)行,<client>方法是由編譯器自動(dòng)收集類中的所有類變量的賦值動(dòng)作和靜態(tài)語(yǔ)句塊中的語(yǔ)句合并而成的。該方法是對(duì)靜態(tài)成員的初始化,包括靜態(tài)變量與靜態(tài)代碼塊。<client>方法執(zhí)行順序?yàn)橄葓?zhí)行父類的<client>再執(zhí)行子類的<client>

1. 靜態(tài)成員初始化順序按類中的聲明順序

2. 靜態(tài)代碼塊只能訪問(wèn)到定義在代碼塊之前的靜態(tài)成員,而定義在靜態(tài)代碼塊后靜態(tài)成員,在前面的靜態(tài)代碼塊中只能賦值。

在接口中沒(méi)有靜態(tài)成員,但也有常量成員。所以存在方法的對(duì)常量初始化,但接口不會(huì)初始化時(shí)不會(huì)馬上調(diào)用方法。只有當(dāng)使用到常量成員才會(huì)執(zhí)行方法。如ClassA繼承了InterfaceA,初始化ClassA時(shí)會(huì)執(zhí)行ClassA的方法,但不會(huì)執(zhí)行InterfaceA的方法,只有使用到InterfaceA的常量才調(diào)用方法。

注:同一個(gè)類加載器下,一個(gè)類型只會(huì)初始化一次

擴(kuò)展<init>與<client>的區(qū)別

<clinit>是虛擬機(jī)在裝載一個(gè)類初始化的時(shí)候調(diào)用的。<init>是在類實(shí)例化時(shí)調(diào)用的

<init>方法是在一個(gè)類進(jìn)行對(duì)象實(shí)例化時(shí)調(diào)用的。實(shí)例化一個(gè)類有四種途徑:調(diào)用new操作符;調(diào)用Class或java.lang.reflect.Constructor對(duì)象的newInstance()方法;調(diào)用任何現(xiàn)有對(duì)象的clone()方法;通過(guò)java.io.ObjectInputStream類的getObject()方法反序列化。

Java編譯器會(huì)為它的每一個(gè)類都至少生成一個(gè)實(shí)例初始化方法。在Class文件中,被稱為"<clinit>"

SO,一個(gè)是用于初始化靜態(tài)的類變量, 一個(gè)是初始化實(shí)例變量!

類加載器


類與類加載器

每個(gè)類加載器都有一個(gè)獨(dú)立的命名空間,一個(gè)類必須與類類加載器結(jié)合在一起才具備唯一性,換句話說(shuō)同一個(gè)類不同類加載器加載都是不一樣的類

引導(dǎo)類加載器

負(fù)載加載\lib下的類,加載虛擬機(jī)中最核心的類,類加載器中頂層加載器

擴(kuò)展類加載器

負(fù)責(zé)加載\lib\ext,加載第三方類庫(kù)

系統(tǒng)類加載器

加載我們應(yīng)用程序的類也就是我們自己編寫(xiě)的類,此外我們還可以自定義類加載器

雙親委派模型

類加載器之間存在父子關(guān)系,不是通過(guò)繼承實(shí)現(xiàn)而是組合關(guān)系來(lái)實(shí)現(xiàn)代碼復(fù)用

當(dāng)一個(gè)類加載器接收到類加載請(qǐng)求,它的程序執(zhí)行步驟如下:

1. 檢查該類是否已經(jīng)加載了

2. 如果沒(méi)有則委派給上一層(父)加載器,父加載器收到加載請(qǐng)求如果自身還有上層則繼續(xù)向上委派請(qǐng)求

3. 直到到達(dá)頂層引導(dǎo)類加載器收到請(qǐng)求,則查詢是否有合適類加載,有則加載,沒(méi)有則交給下一層子加載器加載

4. 子加載器如果找到類則加載,沒(méi)有找到則繼續(xù)往下層委派

5. 最后都沒(méi)有找到就拋出異常

歡迎Q群交流:432550774

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容