本篇內(nèi)容主要講解“Java類加載機制和雙親委派是什么”,感興趣的朋友不妨來看看。本文介紹的方法操作簡單快捷,實用性強。下面就讓小編來帶大家學習“Java類加載機制和雙親委派是什么”吧!
臨滄網(wǎng)站制作公司哪家好,找創(chuàng)新互聯(lián)建站!從網(wǎng)頁設(shè)計、網(wǎng)站建設(shè)、微信開發(fā)、APP開發(fā)、成都響應(yīng)式網(wǎng)站建設(shè)等網(wǎng)站項目制作,到程序開發(fā),運營維護。創(chuàng)新互聯(lián)建站從2013年成立到現(xiàn)在10年的時間,我們擁有了豐富的建站經(jīng)驗和運維經(jīng)驗,來保證我們的工作的順利進行。專注于網(wǎng)站建設(shè)就選創(chuàng)新互聯(lián)建站。
類加載器歷史
在 Java 1.1 及之前的版本中,各個類加載之間不存在聯(lián)系。 例如系統(tǒng)類加載器負責加載應(yīng)用,以及 classpath 目錄下的 class 文件和資源; 而 applet 的類加載器負責和服務(wù)器端交互以加載 applets 應(yīng)用和它相關(guān)的 class 文件和資源。
在 J2SE 1.2 版本(如果你知道 J2SE 這個稱呼,證明你是一名老程序員了,哈哈),類加載器之間產(chǎn)生了一種關(guān)系,這種關(guān)系也就是我們熟知的 parent delegation(中文譯作雙親委派) 機制。
雙親委派是什么
簡單來說雙親外派機制就是當前的類加載器去加載一個 class 數(shù)據(jù)之時,它會先委托它的父加載器去做這件事,父加載器它會遞歸去委托自己的父加載器去加載,直到父加載器不存在,或者父加載器加載不到的時候才自己去加載(注意:此處的父加載器并不是 Java 中的繼承關(guān)系,而是職責上的關(guān)系)。
JDK 中提供了如下 3 種常見的類加載器:
BootstrapClassLoader: 俗稱啟動類加載器,是最頂層的類加載器,也稱為 root 類加載器,負載加載 JRE/lib/rt.jar 中的 class 文件,加載目錄可以通過 -Xbootclasspath 改變。
ExtClassLoader: 俗稱擴展類加載器,負責加載 JRE/lib/ext 目錄下的 class 文件,可以通過設(shè)置環(huán)境變量 java.ext.dirs 改變加載目錄,優(yōu)先級次于 BootstrapClassLoader。
AppClassLoader: 俗稱應(yīng)用類加載器,也稱系統(tǒng)類加載器,負責加載我們的應(yīng)用 class 文件和 classpath 環(huán)境變量指定目錄下的 class 文件,優(yōu)先級次于 ExtClassLoader。
這種機制的好處是可以明確的分工每種類加器的職責,同時保證 class 加載的唯一性,當一個 class 文件被其父加載器加載過以后,后續(xù)類加載器就不會加載了。
雙親委派機制的弊端
它也有不足之處,例如 Java 的 SPI 機制,這種雙親委派機制就不能很好的支持,因此又引入了上下文類加載器。
SPI 全稱 Service Provider Interface,它是 Java 發(fā)現(xiàn)服務(wù)的一種規(guī)范。JDK 負責提供服務(wù)的接口規(guī)范,第三方廠商負責來實現(xiàn)該服務(wù)。例如我們熟知的 JDBC 就是采用這種機制來實現(xiàn)。
JDBC 的接口規(guī)范由 JDK 定義在 rt.jar 中,我們知道這個 jar 中 class 是由 BootstrapClassLoader 來負責加載的,然而 JDBC 的實現(xiàn)類是由 AppClassLoader 來負責加載的。 因此當 JDBC 接口需要用到實現(xiàn)類時就無法完成操作了,但是雞賊的 Java 大神們引入了線程上下文類加載器來解決這個問題。
如果你不做特殊設(shè)置的話,通常線程的上下文類加載器就是系統(tǒng)類加載器,即為 AppClassLoader,使用它恰巧可以加載廠商提供的實現(xiàn)類的 class 文件,有興趣的同學可以參考 JDK 中 java.sql 包下的 DriverManager 中的部分源碼如下:
// Worker method called by the public getConnection() methods.
private static Connection getConnection
(
throws SQLException {
/*
* When callerCl is null, we should check the application's
* (which is invoking this class indirectly)
* classloader, so that the JDBC driver class outside rt.jar
* can be loaded from here.
ClassLoader callerCL = caller != null ? caller.getClassLoader() : null ;
synchronized (DriverManager.class) {
// synchronize loading of the correct classloader.
if (callerCL == null ) {
callerCL = Thread.currentThread().getContextClassLoader();
}
/**省略部分源碼**/
}
通過上面我們了解了 JDK 中幾種類加載器的分工,也討論了雙親委派加載機制的本質(zhì)。 接下來讓我們一起看看一個 class 文件在被加載到 Java 運行時環(huán)境中變成一個可以使用的 java.lang.Class 實例之前經(jīng)過了哪些步驟。
類加載步驟
一個 class 文件變?yōu)?Java 運行時環(huán)境中的可以使用的 Class 實例時,主要經(jīng)過了加載、鏈接和初始化 3 個步驟。
1. 加載
這個階段總共會做 3 件事:
1.通過類的全限定名獲得定義該類的二進制字節(jié)流。
2.將字節(jié)流轉(zhuǎn)換為 JVM 運行時數(shù)據(jù)結(jié)構(gòu)。
3.在 JVM 中生成代表該類的 Class 實例,以供后續(xù)使用。
2. 鏈接
該階段主要分為了驗證、準備和解析 3 個步驟:
驗證 是鏈接第一步,首先驗證文件格式,確認 class 文件否和當前虛擬機規(guī)范,例如以魔數(shù) 0xCAFEBABE 開頭,class 版本號在當前虛擬機處理范圍內(nèi)等等; 其次是分析代碼語義,確認其描述的語義否和 Java 語言規(guī)范;
準備 是鏈接的第二步,該階段將為類變量(static 修飾)分配內(nèi)存,如果它是一個常量(static final 修飾),則直接初始化為目標常量。
解析 是鏈接的第三步,該階段虛擬機會將常量池中符號引用替換為直接引用。
3. 初始化
該階段是最貼近程序員編碼的,主要執(zhí)行所有類變量的初始化和靜態(tài)代碼塊,同時虛擬機會保證在子類初始化操作之前完成父類(接口除外,接口只有在直接使用到接口的靜態(tài)屬性時候才會初始化)的初始化。
到此,相信大家對“Java類加載機制和雙親委派是什么”有了更深的了解,不妨來實際操作一番吧!這里是創(chuàng)新互聯(lián)網(wǎng)站,更多相關(guān)內(nèi)容可以進入相關(guān)頻道進行查詢,關(guān)注我們,繼續(xù)學習!