雙親委派機制:當某個類載入器準備載入一個.class位元組碼文件時,它首先將這個載入任務委派給上一級類載入器,上一級載入器再委派到更上一級類載入器,遞歸這個操作直到最頂級的類載入器。 一、類載入器的類別 在介紹Java的雙親委派機制的時候,不得不提ClassLoader(類載入器) 我們編譯的Java ...
雙親委派機制:當某個類載入器準備載入一個.class位元組碼文件時,它首先將這個載入任務委派給上一級類載入器,上一級載入器再委派到更上一級類載入器,遞歸這個操作直到最頂級的類載入器。
一、類載入器的類別
在介紹Java的雙親委派機制的時候,不得不提ClassLoader(類載入器)
我們編譯的Java代碼是如何在JVM中運行的?首先源程式(.java文件)被Java編譯器編譯為.class位元組碼文件,然後ClassLoader負責將這些class文件載入到JVM中去執行。
如上圖所示:JVM提供了三層類載入器
-
BootStrapClassLoader(引導類載入器)
引導類載入器:用
C++
編寫,是Java自帶的類載入器,用於載入JDK內部的類;主要負責載入核心的類庫,構造ExtensionClassLoader
和AppClassLoader
。Bootstrap類載入器用於載入JDK中$JAVA_HOME/jre/lib下麵的那些類,比如rt.jar包裡面的類。由於引導類載入器涉及到虛擬機本地實現細節,開發者無法直接獲取到引導類載入器的引用,所以不允許直接通過引用進行操作。
-
ExtensionClassLoader(擴展類載入器)
擴展類載入器:主要負責載入 jre/lib/ext 目錄下的一些擴展的jar包或-Djava.ext.dirs指定目錄下的jar包。主要用於載入JDK擴展包里的類。一般$JAVA_HOME/lib/ext下麵的包都是通過這個類載入器載入的
Java
編寫,載入擴展庫,如classpath
中的jre
,javax.*
或者java.ext.dir
指定位置中的類,開發者可以直接使用標準擴展類載入器。 -
AppClassLoader(系統類載入器)
系統類載入器:負責java -classpath或-Djava.class.path所指定目錄下的類與jar包
主要負責載入應用程式的主函數類。用來載入開發人員自己平時寫的應用代碼的類的,載入存放在classpath路徑下的那些應用程式級別的類的。
Java
編寫,載入程式所在的目錄 -
CustomClassLoader(用戶自定義類載入器)
java
編寫,用戶自定義的類載入器,可載入指定路徑的class
文件
二、工作原理
- 如果一個類載入器收到類載入的請求,並不會自己先載入,而是將載入請求委托給父類的載入器進行載入;
- 如果父類的載入器還存在其父類載入器,則進一步向上委托,遞歸這個操作,直至到達最高級的引導類載入器;
- 如果父類載入器可以完成載入請求,則成功返回,如果父類載入器不能完成載入請求,則子載入器才會嘗試自己去載入,這就是雙親委派機制;
- 父類載入器一層層向下分派任務,如果子類載入器能載入則載入此類,如果直到系統類載入器也無法載入,則拋出異常。
從上圖我們就容易理解,當一個Test.class文件要載入時:
-
不考慮用戶自定義的類載入器,首先會在AppClassLoader中檢查是否載入過,如果已經載入過就無需在載入了;如果沒有,就會委派到父載入器,然後調用父載入器的loadClass方法
-
父類載入器同理也會先檢查是否載入過,如果沒有再向上委派
這個類似遞歸的過程,直到到達BootstrapClassLoader之前,都只是檢查是否載入過,並不會選擇自己載入
-
直到BootstrapClassLoader,已經沒有父載入器了,這是開始考慮自己是否能載入了,如果自己不能載入,則下沉到子類載入器去載入,直到最底層
-
如果沒有任何類載入器能夠載入,則拋出ClassNotFoundException異常
三、雙親委派機制的作用
- 防止同一個
.class
文件被重覆載入。通過委派機制向上問一問,如果已經載入過,就不用再載入一次,保證數據安全 - 保證核心
.class
文件不能被篡改。通過委派機制,即使不小心篡改了核心.class
文件,也不會被執行,保證了系統級別的類的安全性
四、代碼淺析
舉例1:建立一個java.lang.String類,寫上static代碼塊
在另外的程式中載入 String 類,看看載入的 String 類是 JDK 自帶的 String 類,還是我們自己編寫的 String 類
從輸出結果可以看到,程式並沒有輸出靜態代碼塊的內容,自定義String類並沒有被載入,所以載入的是JDK內置的String類
這是因為我們自定義的String類本應被AppClassLoader載入,但AppClassLoader並不會先自己載入,而是把載入請求委托給父類的載入器去執行,到了ExtensionClassLoader發現String類不歸自己管,於是委托給父類的載入器(BootStrapClassLoader),引導類載入器發現是java.lang包,所以載入的就是JDK內置的String類
舉例2:在自定義的String類中寫個main()方法
由於雙親委派機制載入的是JDK內置的String類,而在引導類載入器中的核心類庫API里的String類並沒有main()方法
舉例3:在java.lang包下自定義一個AbcTest類
報錯,java.lang包下不允許自定義類