
定義
所謂的類加載就是將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