自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

外婆問我:什么是雙親委派原則?

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

 [[384140]]

本文轉(zhuǎn)載自微信公眾號「三太子敖丙」,作者三太子敖丙。轉(zhuǎn)載本文請聯(lián)系三太子敖丙公眾號。

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

ClassNotFoundException

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

NoClassDefFoundError

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

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

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

什么是Java類加載?

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

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

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

 

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

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

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

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

jvm內(nèi)置classLoader

 

  • **啟動類加載器:**java虛擬機啟動后創(chuàng)建的第一個類加載器,由C++語言實現(xiàn),所以我們在java代碼中查看其信息時,看到的均為null。
  • 擴展類加載器:由啟動類加載器加載,并將擴展類加載器中的parent的值設(shè)置為null(表示指向啟動類加載器),同時繼承自URLClassLoader。
  • 系統(tǒng)類加載器:由啟動類加載器加載,并將系統(tǒng)類加載期中的parent的值設(shè)置為上述創(chuàng)建的擴展類加載器。,同時繼承自URLClassLoader。

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

代碼查看類加載器的parent

 

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

  1. public abstract class ClassLoader { 
  2.    .................... 
  3.     // The parent class loader for delegation 
  4.     private final ClassLoader parent; 

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

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

三種類加載器的加載路徑

 

如上圖所示:

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

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

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

啟動類加載器加載路徑

 

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

支持在JVM啟動之前進行修改路徑,運行中修改路徑不生效,擴展類路徑中僅支持jar包的加載。

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

擴展類加載器路徑

 

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

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

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

 

JVM類加載雙親委托機制

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

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

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

如下圖所示:

雙親委派

 

雙親委托加載實例實例

采用JDK版本

  1. 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) 

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

A_java


TestMain

 

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

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

編譯命令

 

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

委托給擴展類加載器加載

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

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

擴展委派

 

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

擴展委派結(jié)果

 

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

委托給啟動類加載器進行加載

通常情況下,普通類的加載不應(yīng)該委托給啟動類加載器進行加載,因為前面說過啟動類加載器由C++實現(xiàn),在java虛擬機啟動時生成的,在java環(huán)境中獲取她的信息均為null。

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

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

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

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

類存放路徑如圖所示:

委派啟動

 

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

委派啟動結(jié)果

 

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

擴展類加載器又會繼續(xù)進行委托加載(實際上因為擴展類加載器的parent:啟動類加載器為null,所以此時的委托動作實際上就是去啟動類加載器的加載路徑中尋找class A),最終由啟動類加載進行了A的加載。

雙親委托加載方向

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

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

  • 卒子過河比喻當(dāng)前類加載器委派其雙親加載了某個類。這個類的后續(xù)依賴的加載已經(jīng)和當(dāng)前類加載器沒有關(guān)系。
  • 過河之后的卒子只能前進,表示雙親在加載類的依賴類時,只能繼續(xù)遞歸進行雙親委派。
  • 左右平移表示雙親在遞歸雙親委派加載失敗后,在雙親類加載器自己的加載路徑中進行加載。

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

上述委托實例中我們的場景時是:TestMain中依賴了A,我們將A通過雙親委托方式進行了加載,本次實驗中,我們將TestMain委托給雙親加載。

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

委派加載順序1

 

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

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

 

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

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

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

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

總結(jié)這次大致介紹了java的類加載在整個JVM中的作用,詳細(xì)介紹了JVM中的啟動類加載器、擴展類加載器和系統(tǒng)類加載器三者之間的關(guān)系,并結(jié)合實例著重介紹了類加載的雙親委派加載原理,理解java的雙親委派加載原理之后,就能在后續(xù)的程序開發(fā)設(shè)計中在程序的動態(tài)設(shè)計這塊掌握更多高級技能,開發(fā)出更加優(yōu)秀的產(chǎn)品。

 

責(zé)任編輯:武曉燕 來源: 三太子敖丙
相關(guān)推薦

2023-12-06 12:11:43

類加載器雙親委派模型

2023-02-03 07:24:49

雙親委派模型

2023-08-04 08:53:42

2024-06-24 08:24:57

2025-04-07 04:25:00

JDBCAPI加載器

2023-10-30 01:02:56

Java類類加載器雙親委派

2012-06-25 10:05:10

程序員

2021-11-15 12:45:44

協(xié)同過濾算法架構(gòu)

2021-11-23 10:50:29

關(guān)聯(lián)規(guī)則推薦推薦系統(tǒng)開發(fā)

2024-07-05 09:31:37

2021-05-12 16:27:55

Java雙親模型

2024-03-27 09:15:27

2021-11-12 11:51:03

基于內(nèi)容的推薦查詢推薦

2020-03-18 09:31:47

設(shè)計模式軟件

2020-03-30 17:20:54

B+樹SQL索引

2010-01-22 10:36:25

C++語言

2024-12-04 09:01:55

引導(dǎo)類加載器C++

2021-01-06 09:51:19

類加載器雙親委派模型

2023-01-04 00:09:31

2021-03-03 12:19:20

原型原型鏈JavaScript
點贊
收藏

51CTO技術(shù)棧公眾號