本篇內(nèi)容介紹了“什么是類加載器和雙親委派機(jī)制”的有關(guān)知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領(lǐng)大家學(xué)習(xí)一下如何處理這些情況吧!希望大家仔細(xì)閱讀,能夠?qū)W有所成!
創(chuàng)新互聯(lián)公司專注于滿洲網(wǎng)站建設(shè)服務(wù)及定制,我們擁有豐富的企業(yè)做網(wǎng)站經(jīng)驗。 熱誠為您提供滿洲營銷型網(wǎng)站建設(shè),滿洲網(wǎng)站制作、滿洲網(wǎng)頁設(shè)計、滿洲網(wǎng)站官網(wǎng)定制、重慶小程序開發(fā)服務(wù),打造滿洲網(wǎng)絡(luò)公司原創(chuàng)品牌,更為您提供滿洲網(wǎng)站排名全網(wǎng)營銷落地服務(wù)。
我們都知道Java代碼會被編譯成class文件,在class文件中描述了該類的各種信息,class類最終需要被加載到虛擬機(jī)中才能運行和使用。
虛擬機(jī)把Class文件加載到內(nèi)存,并對數(shù)據(jù)進(jìn)行校驗、轉(zhuǎn)換解析和初始化,最終形成虛擬機(jī)可以直接使用的Java類型,這就是虛擬機(jī)的類加載機(jī)制。
一個類從被加載到卸載出內(nèi)存,一共包含下面七個階段:
加載、驗證、準(zhǔn)備、解析、初始化、使用、卸載 加載的來源有以下部分:
1、本地磁盤
2、網(wǎng)絡(luò)下載的.class文件
3、war,jar下加載.class文件
4、從專門的數(shù)據(jù)庫中讀取.class文件(少見)
5、將java源文件動態(tài)編譯成class文件,典型的就是動態(tài)代理,通過運行時生成class文件
加載的過程是通過類加載器實現(xiàn)的。有關(guān)類加載的其他過程我會在下一章中介紹。
類加載器分為系統(tǒng)級別和用戶級別:
系統(tǒng)級別的類加載器有:
1、啟動類加載器(底層使用C++實現(xiàn))
2、擴(kuò)展類加載器(底層使用java實現(xiàn),是ClassLoader的子類)
3、應(yīng)用程序類加載器(底層使用java實現(xiàn),是ClassLoader的子類)
用戶級別的類加載器我們統(tǒng)一稱為自定義類加載器。
首先我們來看看啟動類加載器加載了哪些類,啟動類加載器負(fù)責(zé)加載sun.boot.class.path:
public static void bootClassLoaderLoadingPath(){ //獲取啟動列加載器加載的目錄 String bootStrapLoadingPath=System.getProperty("sun.boot.class.path"); //把加載的目錄轉(zhuǎn)為集合 ListbootLoadingPathList= Arrays.asList(bootStrapLoadingPath.split(";")); for (String bootPath:bootLoadingPathList){ System.out.println("啟動類加載器加載的目錄:"+bootPath); } }
通過上面的代碼我們可以獲取到啟動類加載器所加載的類:
擴(kuò)展類加載器加載負(fù)責(zé)加載java.ext.dirs,我們同樣寫一段代碼去加載它:
public static void extClassLoaderLoadingPath(){ //獲取啟動列加載器加載的目錄 String bootStrapLoadingPath=System.getProperty("java.ext.dirs"); //把加載的目錄轉(zhuǎn)為集合 ListbootLoadingPathList= Arrays.asList(bootStrapLoadingPath.split(";")); for (String bootPath:bootLoadingPathList){ System.out.println("拓展類加載器加載的目錄:"+bootPath); } }
可以看到,除了加載了JDK目錄下的ext外,還加載了Sun目錄下的ext
最后是應(yīng)用類加載器,它負(fù)責(zé)加載java.class.path:
public static void appClassLoaderLoadingPath(){ //獲取啟動列加載器加載的目錄 String bootStrapLoadingPath=System.getProperty("java.class.path"); //把加載的目錄轉(zhuǎn)為集合 ListbootLoadingPathList= Arrays.asList(bootStrapLoadingPath.split(";")); for (String bootPath:bootLoadingPathList){ System.out.println("應(yīng)用程序類加載器加載的目錄:"+bootPath); } }
它負(fù)責(zé)加載工程目錄下classpath下的class以及jar包。
所謂雙親委派模型,就是指一個類接收到類加載請求后,會把這個請求依次傳遞給父類加載器(如果還有的話),如果頂層的父類加載器可以加載,就成功返回,如果無法加載,再依次給子加載器去加載。 我們先通過代碼來看一下類加載器的層級結(jié)構(gòu):
public class ClassLoaderPath { public static void main(String[] args) { System.out.println(ClassLoaderPath.class.getClassLoader()); System.out.println(ClassLoaderPath.class.getClassLoader().getParent()); System.out.println(ClassLoaderPath.class.getClassLoader().getParent().getParent()); } }
編寫一個類,依次輸出這個類的類加載器,父類加載器,父類的父類加載器
可以看到首先是應(yīng)用程序類加載器,它的父類是擴(kuò)展類加載器,擴(kuò)展類加載器的父類輸出了一個null,這個null會去調(diào)用啟動類加載器。如果你不信,我們看源碼:ClassLoader類
接著從父類加載器往下調(diào)用findClass,如果可以加載,就直接返回class,如果不能加載,就依次向下。如果到了自定義加載器還是無法被加載,就會拋出ClassNotFound異常。
我畫了一個流程圖來展示雙親委派模型的全過程:
雙親委派模型保證了Java程序的穩(wěn)定運行,可以避免類的重復(fù)加載,也保證了 Java 的核心 API 不被篡改。
雙親委派模型并不是絕對的,spi機(jī)制就可以打破雙親委派模型。
首先我們需要了解什么是spi,spi(Service Provider Interface)是一種服務(wù)發(fā)現(xiàn)機(jī)制,Java在核心庫中定義了許多接口,并且針對這些接口給出調(diào)用邏輯,但是并未給出具體的實現(xiàn)。開發(fā)者要做的就是定制一個實現(xiàn)類,在 META-INF/services 中注冊實現(xiàn)類信息,以供核心類庫使用。最典型的就是JDBC。
Java提供了一個Driver接口用于驅(qū)動各個廠商的數(shù)據(jù)庫連接,Driver類位于JAVA_HOME中jre/lib/rt.jar中,應(yīng)該由Bootstrap類加載器進(jìn)行加載。根據(jù)類加載機(jī)制,當(dāng)被加載的類引用了另外一個類的時候,虛擬機(jī)就會使用加載該類的類加載器加載被引用的類,因此如果其他數(shù)據(jù)庫廠商定制了Driver的實現(xiàn)類之后,按理說也得把這個實現(xiàn)類放到啟動類加載器加載的目錄下,這顯然是很不合理的。
于是Java提供了spi機(jī)制,即使Driver由啟動類加載器去加載,但是他可以讓線程上下文加載器(Thread Context ClassLoader)去請求子類加載器去完成加載,默認(rèn)是應(yīng)用程序類加載器。但是這確實破壞了類加載機(jī)制。
“什么是類加載器和雙親委派機(jī)制”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識可以關(guān)注創(chuàng)新互聯(lián)網(wǎng)站,小編將為大家輸出更多高質(zhì)量的實用文章!