詳解Java類加載機制中的雙親委派模型
Java中的類加載器
在介紹雙親委派模型之前,首先先介紹下Java中的類加載器。
啟動類加載器(Bootstrap ClassLoader)
首先先介紹下類加載器中的重量級器物,就是大名鼎鼎--啟動類加載器。
為什么說他是重量級的恩,因為它是Java類加載器層次結構的最頂層,由虛擬機實現,用于加載Java核心類庫,如java.lang和java.util等。
既然是最頂層的類加載器,我們就康康它有什么作用:
啟動類加載器是由C/C++編寫的,無法直接在Java代碼中獲取其引用。 它負責加載Java運行時環(huán)境所需的基本類。
擴展類加載器(Extension ClassLoader)
接著我們看一下Java加載器中的第二號人物--擴展類加載器。
擴展類加載器是由Java編寫的,它是啟動類加載器的子類。 它負責加載Java擴展類庫,位于jre/lib/ext路徑下的JAR文件。 擴展類加載器可以通過java.ext.dirs系統屬性來指定其他目錄作為擴展類庫的路徑。
應用程序類加載器(Application ClassLoader)
接著介紹最底層的加載器--應用程序類加載器。
應用程序類加載器是Java類加載器層次結構的最底層,也稱為系統類加載器。 它負責加載應用程序類路徑(Classpath)下的類,包括開發(fā)者自己編寫的類和第三方庫。 應用程序類加載器可以通過-classpath或-cp選項來指定加載類的路徑。
自定義類加載器(Custom ClassLoader)
除了上述系統類的加載器,我們開發(fā)者還可以自定義加載器-驚不驚喜,意不意外。
自定義類加載器是開發(fā)者根據需求編寫的自定義加載器,繼承自ClassLoader類。 它可以根據特定的加載規(guī)則和需求,從不同的來源加載類,比如本地文件系統、網絡等。 自定義類加載器需要實現findClass()方法,指定類的加載規(guī)則,然后通過defineClass()方法加載類的字節(jié)碼。
雙親委派模型
看到這里,上我們的重頭菜,這塊知識是面試中的重點內容。
雙親委派(Parent Delegation)是Java類加載機制中一種重要的實現方式,它通過一種遞歸的方式在類加載器之間建立了父子關系,并且定義了類加載的優(yōu)先級。該模型主要用于解決類加載的沖突和隔離問題,保證Java應用程序的穩(wěn)定性和安全性。
在Java類加載機制中,每個類加載器都有一個父加載器。當一個類加載器需要加載一個類時,它首先會委托給它的父加載器進行加載。只有當父加載器無法加載時,子加載器才會嘗試加載。這種遞歸的委派模型可以形成一個類加載器的層次結構,稱為類加載器樹。
我看了《深入理解Java虛擬機-第三版》中的講解,挺詳細的,這里分享給大家:如果一個類加載器收到了類加載的請求,它首先不會自己去嘗試加載這個類,而是把這個請求委派給父類加載器去完成,每一個層次的類加載器都是如此,因此所有的加載請求最終都應該傳送到最頂層的啟動類加載器中,只有當父類加載器反饋自己無法加載這個請求時,子加載器才會嘗試自己去加載。
雙親委派模型源碼
首先可以先嘗試自己看下源碼,非常通俗易懂:
protected synchronized Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException { // 首先,檢查類是否已經被加載 Class<?> c = findLoadedClass(name); if (c == null) { try { if (parent != null) { // 如果父類加載器存在,則委派給父類加載器 c = parent.loadClass(name, false); } else { // 否則,使用引導類加載器進行加載 c = findBootstrapClassOrNull(name); } } catch (ClassNotFoundException e) { // 如果父類加載器也無法找到類,則嘗試自己加載 c = findClass(name); } } if (resolve) { resolveClass(c); } return c; }
在上面的源碼中,我們可以看到雙親委派機制的體現。當loadClass方法被調用時,首先會檢查該類是否已經被加載。如果沒有加載,那么會按照以下步驟進行處理:
首先,調用findLoadedClass(name)方法檢查類是否已經被其他類加載器加載過。如果是,則直接返回該類的Class對象。
如果這個類還未加載,則嘗試委派給父類加載器加載。通過parent.loadClass(name, false)方法,將類加載任務傳遞給父類加載器。這個過程中,父類加載器會重復執(zhí)行上述流程,直到達到引導類加載器為止。
如果所有的父類加載器都無法加載該類,那么會嘗試使用啟動類加載器加載。這是Java類加載器層次結構的最頂層。
如果引導類加載器仍然無法找到所需的類,就會調用findClass(name)方法在當前類加載器的作用域內查找并加載該類。
到最后,如果resolve參數為true,就會調用resolveClass(c)方法進一步解析和鏈接該類。
如何打破雙親委派模型
其實雙親委派模型并不是具有強制性約束的模型,雖然它有助于保證類的隔離和加載的順序,但有時候我們可能需要打破這種機制,比如我們在特殊情況下需要自定義類加載邏輯或實現熱部署的時候。
上周面試問道了這個問題,所以在這里也介紹幾種打破Java中雙親委派機制的方法:
自定義類加載器:我們自定義一個繼承自ClassLoader的類加載器,重寫loadClass方法,可以實現自己的類加載邏輯。在我們定義的這個方法中,可以在需要時跳過父類加載器并直接加載類,從而打破雙親委派機制。但是,在自定義類加載器中打破雙親委派機制可能會導致類的隔離性和安全性問題,所以我們在使用時要慎重。
線程上下文類加載器:Java中提供了線程上下文類加載器(Thread Context ClassLoader)的概念,它可以在某些情況下打破雙親委派機制。例如,在JNDI、SPI(Service Provider Interface)和一些框架中,線程上下文類加載器可以用來加載指定的類,從而不受雙親委派機制的限制。
通過反射機制:使用反射機制可以直接調用Class類的defineClass方法,這樣可以繞過雙親委派機制直接加載指定的類。但是,在使用這種方式時,我們需要自行處理類加載的順序和依賴關系,因為雙親委派機制不再起作用。
綜上所述,不正確地使用或濫用這些方法可能導致類加載沖突、安全問題以及代碼的不穩(wěn)定性 ,因此我們還需要在明確的需求和充分的理解下使用(面試會背誦就行【手動狗頭】)。
以上就是詳解Java類加載機制中的雙親委派模型的詳細內容,更多關于Java雙親委派模型的資料請關注腳本之家其它相關文章!
相關文章
淺析Java中靜態(tài)代理和動態(tài)代理的應用與區(qū)別
代理模式在我們生活中很常見,而Java中常用的兩個的代理模式就是動態(tài)代理與靜態(tài)代理,這篇文章主要為大家介紹了二者的應用與區(qū)別,需要的可以參考下2023-08-08Java Quartz觸發(fā)器CronTriggerBean配置用法詳解
這篇文章主要介紹了Java Quartz觸發(fā)器CronTriggerBean配置用法詳解,本篇文章通過簡要的案例,講解了該項技術的了解與使用,以下就是詳細內容,需要的朋友可以參考下2021-08-08SpringBoot使用Apache?POI實現導入導出Excel文件
Apache?POI?是一個強大的?Java?庫,用于處理?Microsoft?Office?文檔,下面我們來看看SpringBoot如何使用Apache?POI導入導出Excel文件功能吧2025-01-01