何為雙親委派原則

本篇內(nèi)容主要講解“何為雙親委派原則”,感興趣的朋友不妨來(lái)看看。本文介紹的方法操作簡(jiǎn)單快捷,實(shí)用性強(qiáng)。下面就讓小編來(lái)帶大家學(xué)習(xí)“何為雙親委派原則”吧!

創(chuàng)新互聯(lián)專注于六盤水網(wǎng)站建設(shè)服務(wù)及定制,我們擁有豐富的企業(yè)做網(wǎng)站經(jīng)驗(yàn)。 熱誠(chéng)為您提供六盤水營(yíng)銷型網(wǎng)站建設(shè),六盤水網(wǎng)站制作、六盤水網(wǎng)頁(yè)設(shè)計(jì)、六盤水網(wǎng)站官網(wǎng)定制、微信平臺(tái)小程序開發(fā)服務(wù),打造六盤水網(wǎng)絡(luò)公司原創(chuàng)品牌,更為您提供六盤水網(wǎng)站排名全網(wǎng)營(yíng)銷落地服務(wù)。

我敢打賭大家在開發(fā)過(guò)程中經(jīng)常碰到一些類加載的問(wèn)題,比如:

ClassNotFoundException

Cause: java.lang.ClassNotFoundException: Cannot find class: com.cc.A

NoClassDefFoundError

Cause: java.lang.NoClassDefFoundError: Cannot find class: com.cc.A

上述問(wèn)題均和java類加載有關(guān),如果不清楚JVM中類加載的原理,上述問(wèn)題會(huì)讓人郁悶至極,僥幸在網(wǎng)上找到解決方案也只是暫時(shí)解決問(wèn)題,后續(xù)在另外的場(chǎng)景中碰到又會(huì)繼續(xù)懵逼。

我這篇文章將對(duì) Java  類加載器的雙親委派加載原理進(jìn)行闡述,并結(jié)合實(shí)例程序深究類的雙親委派加載機(jī)制,大家徹底了解掌握類加載原理,清楚了類加載原理后,碰到上述類似問(wèn)題就能快速解決,并在后續(xù)開發(fā)中避免類似問(wèn)題。

什么是Java類加載?

java類加載器負(fù)責(zé)將編譯好的 Java class 件加載到 Java  虛擬機(jī)(JVM)中的運(yùn)行時(shí)數(shù)據(jù)區(qū)中,供執(zhí)行引擎調(diào)用。

java類加載在JVM體系結(jié)構(gòu)中的位置如圖所示:

何為雙親委派原則

jvm體系結(jié)構(gòu)原圖

沒有類加載機(jī)制,編寫的java程序就沒法在JVM中運(yùn)行,因此掌握java類加載是非常重要的。

JVM類加載層級(jí)關(guān)系

執(zhí)行java程序時(shí),會(huì)啟動(dòng)一個(gè)JVM進(jìn)程,JVM在啟動(dòng)時(shí)會(huì)做一些初始化操作,比如獲取系統(tǒng)參數(shù)等等,然后創(chuàng)建一個(gè)啟動(dòng)類加載器,用于加載JVM運(yùn)行時(shí)必須的一些類到內(nèi)存中,同時(shí)也會(huì)創(chuàng)建其他兩個(gè)類加載器擴(kuò)展類加載器和系統(tǒng)類加載器。

啟動(dòng)類加載器、擴(kuò)展類加載器和系統(tǒng)類加載器之間的關(guān)系如下圖所示:

何為雙親委派原則

jvm內(nèi)置classLoader

  • **啟動(dòng)類加載器:**java虛擬機(jī)啟動(dòng)后創(chuàng)建的第一個(gè)類加載器,由C++語(yǔ)言實(shí)現(xiàn),所以我們?cè)趈ava代碼中查看其信息時(shí),看到的均為null。

  • 擴(kuò)展類加載器:由啟動(dòng)類加載器加載,并將擴(kuò)展類加載器中的parent的值設(shè)置為null(表示指向啟動(dòng)類加載器),同時(shí)繼承自URLClassLoader。

  • 系統(tǒng)類加載器:由啟動(dòng)類加載器加載,并將系統(tǒng)類加載期中的parent的值設(shè)置為上述創(chuàng)建的擴(kuò)展類加載器。,同時(shí)繼承自URLClassLoader。

在代碼中可以通過(guò)如下方式查看類加載中的parent指向:

何為雙親委派原則

代碼查看類加載器的parent

注意:這里的parent不是java的繼承機(jī)制,而是類加載器中的一個(gè)實(shí)例屬性,用于在類加載時(shí)的委托對(duì)象,parent屬性定義在其所繼承的ClassLoader中,定義如下所示。

public abstract class ClassLoader {    ....................     // The parent class loader for delegation     private final ClassLoader parent;

JVM類加載的默認(rèn)加載路徑

每種類型的類加載器默認(rèn)都會(huì)有自己的加載路徑,啟動(dòng)類加載器、擴(kuò)展類加載器和系統(tǒng)類加載器的默認(rèn)加載路徑如下圖所示:

何為雙親委派原則

三種類加載器的加載路徑

如上圖所示:

1、啟動(dòng)類加載器(BootClassLoader)由C++語(yǔ)言編寫,負(fù)責(zé)在JVM啟動(dòng)時(shí)加載jdk自身的一些核心class類(jar包形式)到JVM中,加載時(shí)尋找資源的路徑由只讀系統(tǒng)屬性:”sun.boot.class.path“  指定,一般為:”JAVA_HOME/jre/classes“目錄(在該目錄下只能放class文件,jar包形式文件不生效)。

查看啟動(dòng)類加載類加載路徑可以通過(guò)獲取系統(tǒng)屬性:”sun.boot.class.path“進(jìn)行查看,如圖所示:

何為雙親委派原則

lancher中設(shè)置啟動(dòng)類加載路徑

何為雙親委派原則

啟動(dòng)類加載器加載路徑

2、擴(kuò)展類加載器(ExtClassLoader),負(fù)責(zé)加載位于系統(tǒng)屬性:"java.ext.dirs"指向的目錄下加載class文件(jar包或者直接class文件形式)到JVM中,比如通常ext類加載路徑為:”$JAVA_HOMEx/jre/lib/ext“  。

支持在JVM啟動(dòng)之前進(jìn)行修改路徑,運(yùn)行中修改路徑不生效,擴(kuò)展類路徑中僅支持jar包的加載。

查看擴(kuò)展類加載器的類加載路徑可以通過(guò)獲取系統(tǒng)屬性:”java.ext.dirs“進(jìn)行查看或向上轉(zhuǎn)型為URLClassLoader(上面說(shuō)擴(kuò)展類加載器繼承自URLClassLoader),查看位于父類URLClassLoader中urls屬性的方式進(jìn)行查看,如圖所示:

何為雙親委派原則

擴(kuò)展類加載器路徑

3、系統(tǒng)類加載器(AppClassLoader),負(fù)責(zé)加載應(yīng)用classpath路徑下的class文件(jar包或者直接class文件形式)到JVM中,當(dāng)系統(tǒng)中沒有設(shè)置classpath路徑時(shí),默認(rèn)加載當(dāng)前路徑下的class文件。

查看系統(tǒng)類加載器的類加載路徑可以通過(guò)獲取系統(tǒng)屬性:”java.class.path“進(jìn)行查看或向上轉(zhuǎn)型為URLClassLoader上面說(shuō)擴(kuò)展類加載器繼承自URLClassLoader),查看位于父類URLClassLoader中urls屬性的方式進(jìn)行查看,如圖所示:

何為雙親委派原則

系統(tǒng)類加載路徑

JVM類加載雙親委托機(jī)制

JVM加載class類文件到虛擬機(jī)時(shí),默認(rèn)首先采用系統(tǒng)類加載器去加載用到的class類,采用的是雙親委托加載機(jī)制。

所謂雙親委托,顧名思義,就是當(dāng)前類加載器(以系統(tǒng)類加載器為例)在加載一個(gè)類時(shí),委托給其雙親(注意這里的雙親指的是類加載器中parent屬性指向的類加載器)先進(jìn)行加載。

雙親類加載器在加載時(shí)同樣委托給自己的雙親,如此反復(fù),直到某個(gè)類加載器沒有雙親為止(通常情況下指雙親為null,也即為當(dāng)前的雙親為擴(kuò)展類加載器,其parent為啟動(dòng)類加載器),然后開始在依次在各自的類路徑下尋找、加載class類。

如下圖所示:

何為雙親委派原則

雙親委派

雙親委托加載實(shí)例實(shí)例

采用JDK版本

java version "1.8.0_261" Java(TM) SE Runtime Environment (build 1.8.0_261-b12) Java HotSpot(TM) 64-Bit Server VM (build 25.261-b12, mixed mode)

本實(shí)例涉及到兩個(gè)類:TestMain.java 和  A.java,期中TestMain為啟動(dòng)類,在啟動(dòng)類中調(diào)用類A中的方法執(zhí)行進(jìn)行輸出,分別輸出啟動(dòng)類和被依賴類的類加載器信息,類定義如下所示:

何為雙親委派原則

A_java

何為雙親委派原則
TestMain

我們將兩個(gè)java文件拷貝到某個(gè)目錄下,在我本地比如放在E:\java_app目錄下,windows下打開命令行窗口,切換到E:\java_app,對(duì)當(dāng)前java文件進(jìn)行編譯,執(zhí)行命令javac  TestMain.java。

此時(shí)會(huì)在當(dāng)前目錄下生產(chǎn)對(duì)應(yīng)的class文件(這里只需要對(duì)TestMain執(zhí)行編譯命令,因?yàn)門estMain依賴了A,所以Jdk編譯器就會(huì)自動(dòng)先去編譯依賴的A),如圖所示:

何為雙親委派原則

編譯命令

接下來(lái)我們將觀察java類加載機(jī)制是怎樣實(shí)現(xiàn)雙親委托加載的。

委托給擴(kuò)展類加載器加載

由于擴(kuò)展類在自身類路徑下加載只支持尋找jar包的方式,因此我們通過(guò)工具將A.class文件打包進(jìn)A.jar。

然后將A.jar放置到擴(kuò)展類加載路徑:$JAVA_HOME/jre/lib/ext,同時(shí)保留當(dāng)前目錄中的A.class文件。如圖所示:

何為雙親委派原則

擴(kuò)展委派

此時(shí)在當(dāng)前目錄:E:\java_app下仍然保留有A.class文件,在擴(kuò)展類加載器路徑下多了一個(gè)包含了A.class的A.jar文件,在當(dāng)前目錄下執(zhí)行java命令執(zhí)行TestMain,命令為:java  TestMain,輸出如下所示:

何為雙親委派原則

擴(kuò)展委派結(jié)果

由上圖輸出結(jié)果可知,class  A雖然在系統(tǒng)類加載器的加載路徑中,但由于類加載的委托機(jī)制,A首先將由系統(tǒng)類加載器委托給其雙親擴(kuò)展類加載器進(jìn)行加載,剛好在擴(kuò)展類加載器的加載路徑中包含了A.class(包含在A.jar中),所以A最終由擴(kuò)展類加載器進(jìn)行了加載。

委托給啟動(dòng)類加載器進(jìn)行加載

通常情況下,普通類的加載不應(yīng)該委托給啟動(dòng)類加載器進(jìn)行加載,因?yàn)榍懊嬲f(shuō)過(guò)啟動(dòng)類加載器由C++實(shí)現(xiàn),在java虛擬機(jī)啟動(dòng)時(shí)生成的,在java環(huán)境中獲取她的信息均為null。

本實(shí)例為了探究類加載的雙親委托機(jī)制,所以特意將構(gòu)造一個(gè)將普通類委托給其加載的場(chǎng)景。

前面在講到啟動(dòng)類加載器加載路徑時(shí)指出了啟動(dòng)類加載器的加載路徑由只讀系統(tǒng)屬性”sun.boot.class.path“  指定,且僅支持加載該目錄下固定的jar文件。

在jdk8中還有”$JAVA_HOME/jre/classes“目錄也是啟動(dòng)類加載器加載的路徑(該路徑默認(rèn)可能不存在,可以手工創(chuàng)建一個(gè)),在該目錄下只能放class文件,jar包形式文件不生效。

因此,本實(shí)例程序?qū)?dāng)前目錄下的A.class文件拷貝到啟動(dòng)類加載器的類路徑:”$JAVA_HOME/jre/classes“中,同時(shí)保留當(dāng)前目錄中的A.class文件,也保留擴(kuò)展類加載器類路徑中的A.jar。

類存放路徑如圖所示:

何為雙親委派原則

委派啟動(dòng)

在當(dāng)前目錄:E:\java_app目錄下執(zhí)行命令運(yùn)行TestMain,命令為:java TestMain,輸出如下所示:

何為雙親委派原則

委派啟動(dòng)結(jié)果

由上圖輸出結(jié)果可知,class  A雖然在系統(tǒng)類加載器的加載路徑中,也存在擴(kuò)展類加載器的加載路徑中,但由于類加載的委托機(jī)制,A首先將由系統(tǒng)類加載器委托給其雙親擴(kuò)展類加載器進(jìn)行加載。

擴(kuò)展類加載器又會(huì)繼續(xù)進(jìn)行委托加載(實(shí)際上因?yàn)閿U(kuò)展類加載器的parent:?jiǎn)?dòng)類加載器為null,所以此時(shí)的委托動(dòng)作實(shí)際上就是去啟動(dòng)類加載器的加載路徑中尋找class  A),最終由啟動(dòng)類加載進(jìn)行了A的加載。

雙親委托加載方向

類加載器在加載類時(shí),只能向上遞歸委托其雙親進(jìn)行類加載,而不可能從雙親再反向委派當(dāng)前類加載器來(lái)進(jìn)行類加載。

在中國(guó)象棋中,卒子過(guò)河之后的行走軌跡永遠(yuǎn)只能是前進(jìn)或者左右平移,可以很形象的比作雙親委托類加載的這種方向性。

  • 卒子過(guò)河比喻當(dāng)前類加載器委派其雙親加載了某個(gè)類。這個(gè)類的后續(xù)依賴的加載已經(jīng)和當(dāng)前類加載器沒有關(guān)系。

  • 過(guò)河之后的卒子只能前進(jìn),表示雙親在加載類的依賴類時(shí),只能繼續(xù)遞歸進(jìn)行雙親委派。

  • 左右平移表示雙親在遞歸雙親委派加載失敗后,在雙親類加載器自己的加載路徑中進(jìn)行加載。

為了表明委派具有方向性,我們繼續(xù)拿上面的TestMain.class和A.class兩個(gè)類做實(shí)驗(yàn)。

上述委托實(shí)例中我們的場(chǎng)景時(shí)是:TestMain中依賴了A,我們將A通過(guò)雙親委托方式進(jìn)行了加載,本次實(shí)驗(yàn)中,我們將TestMain委托給雙親加載。

參照上述的操作步驟,將TestMain.class打進(jìn)TestMain.jar中,放到擴(kuò)展類加載器的加載路徑中,同時(shí)也保留TestMain.class到當(dāng)前目錄,如下圖所示:

何為雙親委派原則

委派加載順序1

切換到當(dāng)前應(yīng)用目錄,執(zhí)行java命令運(yùn)行程序:java TestMain,執(zhí)行結(jié)果如下所示:

何為雙親委派原則

委派順序執(zhí)行結(jié)果

如上圖所示,出現(xiàn)錯(cuò)誤了,TestMain被擴(kuò)展類加載器加載了,依賴的A卻沒有能被加載到。

原因就是上述說(shuō)的委派加載具有方向性導(dǎo)致的:

1、運(yùn)行java命令執(zhí)行TestMain程序時(shí),系統(tǒng)類加載器準(zhǔn)備加載TestMain,根據(jù)雙親委派機(jī)制,先委派給其雙親進(jìn)行加載,最后,雙親擴(kuò)展類加載器在其加載路徑中的TestMain.jar中找到了TestMain.class,完成了TestMain的加載。

2、TestMain中依賴了A,此時(shí),會(huì)根據(jù)加載了TestMain的類加載器:擴(kuò)展類加載器去加載A,加載方式根據(jù)委托機(jī)制遞歸委托給雙親加載,擴(kuò)展類加載器的雙親為啟動(dòng)類加載器,在啟動(dòng)類加載器的加載路徑中不存在A,加載失敗,此時(shí)由擴(kuò)展類加載器在自己的加載路徑中加載A,也因?yàn)榧虞d路徑中沒有A.class存在,A.class存在于系統(tǒng)類加載器的加載路徑中,但是擴(kuò)展類加載器不會(huì)再返回去委托系統(tǒng)類加載器進(jìn)行加載,所以直接拋出加載失敗異常,出現(xiàn)了上述的錯(cuò)誤。

到此,相信大家對(duì)“何為雙親委派原則”有了更深的了解,不妨來(lái)實(shí)際操作一番吧!這里是創(chuàng)新互聯(lián)網(wǎng)站,更多相關(guān)內(nèi)容可以進(jìn)入相關(guān)頻道進(jìn)行查詢,關(guān)注我們,繼續(xù)學(xué)習(xí)!

網(wǎng)頁(yè)題目:何為雙親委派原則
文章鏈接:http://www.muchs.cn/article6/jpjgog.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供定制開發(fā)、網(wǎng)頁(yè)設(shè)計(jì)公司、網(wǎng)站維護(hù)、全網(wǎng)營(yíng)銷推廣、軟件開發(fā)、微信小程序

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)

網(wǎng)站托管運(yùn)營(yíng)