類的加載是每個Java程序必經(jīng)的流程,了解類的加載機制有助于我們寫出更好的代碼。
基礎(chǔ)
概念
類的加載把描述類的數(shù)據(jù)從Class文件加載到內(nèi)存,并對數(shù)據(jù)進行校驗县袱、轉(zhuǎn)換解析和初始化,最終形成可以被虛擬機直接使用的Java類型佑力。
在Java語言里式散,類型的加載、連接和初始化過程都是在程序運行期間完成的打颤。顯然暴拄,這樣會增加一些運行時的開銷,不過也為Java應(yīng)用程序提供了高度的靈活性编饺,使Java天生支持動態(tài)擴展揍移。
類的生命周期
類的生命周期有7部分:加載,驗證反肋,準(zhǔn)備那伐,解析,初始化石蔗,使用和卸載罕邀。
其中驗證,準(zhǔn)備养距,解析3個部分統(tǒng)稱為連接诉探。
具體過程
加載
加載(Loading)是類加載的第一步,就是將字節(jié)碼加載到JVM內(nèi)存中棍厌。官方說法如下:
- 通過一個類的全限定名來獲取定義此類的二進制字節(jié)流;
- 將這個字節(jié)流所代表的靜態(tài)存儲結(jié)構(gòu)轉(zhuǎn)化為方法區(qū)的運行時數(shù)據(jù)結(jié)構(gòu)肾胯;
- 在內(nèi)存中生成一個代表這個類的java.lang.Class對象,作為方法區(qū)這個類的各種數(shù)據(jù)的訪問入口耘纱。
注:JVM在加載數(shù)組的時候加載的僅僅是數(shù)組的類型類(例如String[] 加載器只會加載String這個類型類)敬肚,而數(shù)組的創(chuàng)建則由JVM直接完成。
類加載器
為了完成加載過程中的第一條:”通過一個類的全限定名來獲取定義此類的二進制字節(jié)流”的功能束析,JVM團隊開發(fā)了一個模塊——類加載器艳馒。但是為了給用戶提供更好的拓展性JVM團隊將這個過程的代碼放到了JVM的外部,以便讓開發(fā)人員可以自定義類加載器。
注:對于任意一個類弄慰,他的唯一決定方式是:類本身+加載此類的類加載器第美。通俗的講就是:一個類java.lang.Object 如果被兩個類加載器加載,那么這個兩個類就是不相同的陆爽。
加載器的類型
從Java虛擬機的角度看什往,只有兩種不同的類加載器:
- 啟動類加載器(Bootstrap ClassLoader):用C++實現(xiàn),是虛擬機自身的一部分慌闭;
- 所有其他的類加載器:用Java語言實現(xiàn)恶守,獨立于虛擬機外部,都繼承自抽象類java.lang.ClassLoader;
從Java開發(fā)人員看贡必,類加載器可分為3種:
- 啟動類加載器(Bootstrap ClassLoader):負責(zé)加載<\JAVA——HOME>\lib目錄中的并且可以被虛擬機識別的;
- 擴展類加載器(Extension ClassLoader):負責(zé)加載<\JAVA_HOME>\lib\ext目錄中的所有類庫庸毫,開發(fā)者可以直接使用擴展類加載器仔拟;
- 應(yīng)用程序類加載器(Application ClassLoader):它是ClassLoader中的getSystemClassLoader()方法的返回值,所以也稱它為系統(tǒng)類加載器飒赃。他負責(zé)加載用戶類路徑(ClassPath)上所指定的類庫利花。
雙親委派模型
那么,問題來了载佳,如果用戶自定義了一個Object類放在classpath目錄下炒事,那么加載的時候會用哪個加載器呢?為了解決這個問題蔫慧,JVM團隊就提出了雙親委派模型挠乳。
首先我們要知道,除了啟動類加載器以外姑躲,其余的加載器都有自己的父類加載器睡扬。繼承關(guān)系如下圖:
雙親委派模型就是,如果一個類加載器收到加載請求黍析,他不會馬上加載類卖怜,而是將這個請求向上傳遞給他的父加載器,看父加載器能不能加載這個類阐枣,加載的原則就是優(yōu)先父加載器加載马靠,若果父加載器加載不了,自己才能加載蔼两。
驗證
驗證是連接階段的第一步甩鳄,目的是為了確保Class文件的字節(jié)流中包含的信息符合當(dāng)前虛擬機的要求,并且不會危害虛擬機自身的安全额划。
主要有4個部分:
a. 文件格式驗證
驗證字節(jié)流是否符合Class文件格式的規(guī)范娩贷,并且能被當(dāng)前版本的虛擬機處理。
b. 元數(shù)據(jù)驗證
對類的元數(shù)據(jù)信息進行語義校驗锁孟,是否不存在不符合Java語言規(guī)范的元數(shù)據(jù)信息
c. 字節(jié)碼驗證
最復(fù)雜的一個階段彬祖,主要目的是通過數(shù)據(jù)流和控制流分析茁瘦,確定程序語義是合法的,符合邏輯的储笑。對類的方法體進行校驗分析甜熔,保證被校驗類的方法在運行時不會做出危害虛擬機安全的事件。
d. 符號引用驗證
最后一個階段的校驗發(fā)生在虛擬機將符號引用轉(zhuǎn)換為直接引用的時候突倍,這個轉(zhuǎn)換動作將在連接的第三個階段——解析階段中發(fā)生腔稀。
準(zhǔn)備
類的準(zhǔn)備階段負責(zé)為類的靜態(tài)變量分配內(nèi)存,并設(shè)置默認初始值羽历。
解析
解析是虛擬機將常量池內(nèi)的符號引用替換為直接引用的過程焊虏。
初始化
初始化是類的加載的最后一步,會執(zhí)行類構(gòu)造器<clinit>()方法秕磷。
<clinit>()方法是由編譯器自動收集類中的所有類變量的賦值動作和靜態(tài)語句塊中的語句合并產(chǎn)生的诵闭。
簡單點說初始化就是為類變量進行賦值和執(zhí)行靜態(tài)代碼塊。