深入理解Java中的類加載器原理
一、類加載機制
類加載器負責加載所有的類,系統(tǒng)為所有被載入內(nèi)存中的類生成一個java.lang.Class實例。一旦一個類被載入JVM中,同一個類就不會被再次載入了。在JVM中,一個類用其全限定類名和其類加載器作為唯一標識。
當JVM啟動時,會形成由三個類加載器組成的初始類加載器層次結(jié)構(gòu):
- Bootstrap ClassLoader:根類加載器,加載jre/lib/rt.jar、charset.jar等核心類,由C++實現(xiàn);
- Extension ClassLoader:擴展類加載器,加載擴展jar包,jre/lib/ext/*.jar,或由-Djava.ext.dirs指定;
- System ClassLoader:系統(tǒng)類加載器,加載classpath指定內(nèi)容。
Bootstrap ClassLoader被稱為引導(dǎo)(也成為原始或根)類加載器,它負責加載Java的核心類。在Sun的JVM中,當執(zhí)行java.exe命令時,使用-Xbootclasspath或-D選項指定sun.boot.class.path系統(tǒng)屬性值可以指定加載附加的類。
JVM的類加載機制主要有如下三種:
- 全盤負責:所謂全盤負責,就是當一個類加載器負責加載某個Class時,該Class所依賴的和引用的其他Class也將由該類加載器負責載入,除非顯示使用另外一個類加載器來載入。
- 父類委托:所謂父類委托,則是先讓parent(父)類加載器試圖加載該Class,只有在父類加載器無法加載該類時才嘗試從自己的類路徑中加載該類。
- 緩存機制:緩存機制將會保證所有加載過的Class都會被緩存,當程序中需要使用某個Class時,類加載器先從緩存區(qū)中搜尋該Class,只有當緩存區(qū)中不存在該Class對象時,系統(tǒng)才會讀取該類對應(yīng)的二進制數(shù)據(jù),并將其轉(zhuǎn)換成Class對象,存入到緩存區(qū)中。這就是為什么修改了Class后,必須重新啟動JVM,程序所做的修改才會生效的原因。
注:類加載器之間的父子關(guān)系并不是類繼承上的父子關(guān)系,這里的父子關(guān)系是類加載器實例之間的關(guān)系。
除了可以使用Java提供的類加載器之外,也可以通過繼承ClassLoader來實現(xiàn)自定義的類加載器。
JVM的根類加載器不是Java實現(xiàn)的,而且由于程序通常無須訪問根類加載器,因此訪問擴展類加載器的父類加載器時返回null。但實際上,擴展類加載器的父類加載器時根類加載器。
jdk8中,系統(tǒng)類加載器時AppClassLoader的實例,擴展類加載器是ExtClassLoader的實例。實際上,這兩個類都繼承URLClassLoader類0,因此也都是URLClassLoader類的實例。
類加載器加載Class大致要經(jīng)過如下8個步驟:
- 檢測此Class是否載入過(即在緩存區(qū)中是否有此Class),如果有則直接進入第8步,否則接著執(zhí)行第2步;
- 如果父類加載器不存在(如果沒有父類加載器,則要么parent一定是根類加載器,要么本身就是根類加載器),則直接執(zhí)行第3步;如果父類加載器存在,則繼續(xù)執(zhí)行第2步;
- 請求使用根類加載器來載入目標類,如果成功載入則跳到第9步,如果未成功若當前類加載器就是根類加載器則跳到第8步,否則接著執(zhí)行第4步;
- 請求使用擴展類加載器來載入目標類,如果成功載入則跳到第9步,如果未成功若當前類加載器就是擴展類加載器則跳到第8步,否則接著執(zhí)行第5步;
- 請求使用系統(tǒng)類加載器來載入目標類,如果成功載入則跳到第9步,如果未成功若當前類加載器就是系統(tǒng)類加載器則跳到第8步,否則接著執(zhí)行第6步;
- 當前類加載器嘗試尋找Class文件(從與此ClassLoader相關(guān)的類路徑中尋找),如果找到則執(zhí)行第7步,如果找不到則跳到第8步;
- 從文件中載入Class,成功載入后跳到第8步;
- 拋出ClassNotFoundException異常;
- 返回對應(yīng)的java.lang.Class對象。
注:其中,第6、7步允許重寫ClassLoader的findClass()方法來實現(xiàn)自己的載入策略,甚至重寫loadClass()方法來實現(xiàn)自己的載入過程。
下圖為類加載器加載Class過程的流程圖:

類加載器加載Class流程
二、雙親委派機制
JVM中類加載采用雙親委派機制,所謂雙親委派就是當一個類加載器收到類加載請求時他不會直接去加載目標類,而是把該請求委托給父類加載器去加載。只有當你父類加載器無法加載目標類時,才會交由當前類加載器加載目標類。
采用雙親委派機制,保證了安全性,避免核心類庫被自定義類加載器加載,也避免了類被重復(fù)加載。
下圖為雙親委派機制的流程圖。

三、自定義類加載器
JVM中除根類加載器之外的所有類加載器都是ClassLoader子類的實例,可以通過擴展ClassLoader的子類,并重寫該ClassLoader所包含的方法來實現(xiàn)自定義的類加載器。
ClassLoader中包含大量的protected方法——這些方法都可被子類重寫。
ClassLoader類有兩個關(guān)鍵方法:
- findClass(String name):根據(jù)指定名稱來查找類。
- loadClass(String name, boolean resolve):該方法為ClassLoader的入口點,根據(jù)指定名稱來加載類,系統(tǒng)就是調(diào)用ClassLoader的該方法來獲取指定類對應(yīng)的Class對象。
loadClass()方法的執(zhí)行步驟如下:
①用findLoadedClass(String)來檢查是否已經(jīng)加載類,如果已經(jīng)加載則直接返回。
②在父類加載器上調(diào)用loadClass()方法。如果父類加載器為null,則使用根類加載器來加載。
③調(diào)用findClass(String)方法查找類。
從上面步驟可以看出,重寫findClass()方法可以避免覆蓋默認類加載器的父類委托、緩沖機制兩種策略;如果重寫loadClass()方法則實現(xiàn)邏輯更為復(fù)雜。
ClassLoader里還有一個核心方法:Class defineClass(String name, byte[] b, int off, int len),該方法負責將指定類的字節(jié)碼文件(即Class文件)讀入字節(jié)數(shù)組byte[] b內(nèi),并把它轉(zhuǎn)換為Class對象,該字節(jié)碼文件可以來源于文件、網(wǎng)絡(luò)等、defineClass()方法管理JVM的許多復(fù)雜的實現(xiàn),它負責將字節(jié)碼分析成運行時數(shù)據(jù)結(jié)構(gòu),并校驗有效性等。該方法是final的,不可重寫。
除此之外,ClassLoader里還包含如下一些普通方法。
- findSystemClass(String name):從本地文件系統(tǒng)裝入文件。它在本地文件系統(tǒng)中尋找類文件,如果存在,就使用defineClass()方法將原始字節(jié)轉(zhuǎn)換成Class對象,以將該文件轉(zhuǎn)換成類;
- static getSystemClassLoader():返回系統(tǒng)類加載器;
- getParent():獲取該類加載器的父類加載器;
- resolveClass(Class<?> c):鏈接指定的類。類加載器可以使用此方法來鏈接類c;
- findLoadedClass(String name):如果此Java虛擬機已經(jīng)加載了名為name的類,則直接返回該類對應(yīng)的Class實例,否則返回null。該方法是Java類加載緩存機制的體現(xiàn)。
四、URLClassLoader類
Java為ClassLoader提供了一個URLClassLoader實現(xiàn)類,該類也是系統(tǒng)類加載器和擴展類加載器的父類(此處的父類,就是指類與類之間的繼承關(guān)系)。URLClassLoader功能比較強大,它既可以從本地文件系統(tǒng)獲取二進制文件來加載類,也可以從遠程主機獲取二進制文件來加載類。
在應(yīng)用程序中可以直接使用URLClassLoader加載類,URLClassLoader類提供了如下兩個構(gòu)造器。
- URLClassLoader(URL[] urls):使用默認的父類加載器創(chuàng)建一個ClassLoader對象,該對象將從urls所指定的系列路徑來查詢并加載類。
- URLClassLoader(URL[] urls, ClassLoader parent):使用指定的父類加載器創(chuàng)建一個ClassLoader對象,其他功能與前一個構(gòu)造器相同。
一旦得到了URLClassLoader對象之后,就可以調(diào)用該對象的loadClass()方法來加載指定類。
到此這篇關(guān)于深入理解Java中的類加載器原理的文章就介紹到這了,更多相關(guān)Java類加載器內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Spring Data Redis 中的 opsFor 方法深入解析
opsFor是RedisTemplate類中的一系列方法,用于獲取特定數(shù)據(jù)類型的操作接口,這些方法返回的是 Operations 對象,每個對象都提供了針對特定 Redis 數(shù)據(jù)類型的操作方法,本文重點介紹Spring Data Redis 中的opsFor方法,感興趣的朋友一起看看吧2025-12-12
Spring Boot中進行 文件上傳和 文件下載功能實現(xiàn)
開發(fā)Wb應(yīng)用時,文件上傳是很常見的一個需求,瀏覽器 通過 表單形式 將 文件 以 流的形式傳遞 給 服務(wù)器,服務(wù)器再對上傳的數(shù)據(jù)解析處理,下面將通過一個案例講解使用 SpringBoot 實現(xiàn) 文件上傳,感興趣的朋友一起看看吧2024-07-07
Java初學(xué)者了解"=="與equals的區(qū)別
這篇文章主要介紹了Java初學(xué)者了解"=="與equals的區(qū)別,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下2019-11-11
SpringBoot中間件ORM框架實現(xiàn)案例詳解(Mybatis)
這篇文章主要介紹了SpringBoot中間件ORM框架實現(xiàn)案例詳解(Mybatis),本篇文章提煉出mybatis最經(jīng)典、最精簡、最核心的代碼設(shè)計,來實現(xiàn)一個mini-mybatis,從而熟悉并掌握ORM框架的涉及實現(xiàn),需要的朋友可以參考下2023-07-07
將本地jar包安裝進入maven倉庫(實現(xiàn)方法)
下面小編就為大家?guī)硪黄獙⒈镜豭ar包安裝進入maven倉庫(實現(xiàn)方法)。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-06-06
老生常談Java中instanceof關(guān)鍵字的理解
java 中的instanceof 運算符是用來在運行時指出對象是否是特定類的一個實例。這篇文章主要介紹了老生常談Java中instanceof關(guān)鍵字的理解,需要的朋友可以參考下2018-10-10

