類加載器層次是怎麼樣的
-
JVM預定義的三種類型類加載器:
- 啓動(Bootstrap)類加載器:是用本地代碼實現的類裝入器,它負責將
<Java_Runtime_Home>/lib
下面的類庫加載到內存中(比如rt.jar
)。由於引導類加載器涉及到虛擬機本地實現細節,開發者無法直接獲取到啓動類加載器的引用,所以不允許直接通過引用進行操作。 - 標準擴展(Extension)類加載器:是由 Sun 的
ExtClassLoader(sun.misc.Launcher$ExtClassLoader)
實現的。它負責將< Java_Runtime_Home >/lib/ext
或者由系統變量java.ext.dir
指定位置中的類庫加載到內存中。開發者可以直接使用標準擴展類加載器。 - 系統(System)類加載器:是由 Sun 的
AppClassLoader(sun.misc.Launcher$AppClassLoader)
實現的。它負責將系統類路徑(CLASSPATH
)中指定的類庫加載到內存中。開發者可以直接使用系統類加載器。
除了以上列舉的三種類加載器,還有一種比較特殊的類型 — 線程上下文類加載器。
- 啓動(Bootstrap)類加載器:是用本地代碼實現的類裝入器,它負責將
雙親委派模型是什麼?有什麼好處?
某個特定的類加載器在接到加載類的請求時,首先將加載任務委託給父類加載器,依次遞歸,如果父類加載器可以完成類加載任務,就成功返回;只有父類加載器無法完成此加載任務時,才自己去加載。
委託機制的意義 — 防止內存中出現多份同樣的字節碼
比如兩個類A和類B都要加載System類:
- 如果不用委託而是自己加載自己的,那麼類A就會加載一份System字節碼,然後類B又會加載一份System字節碼,這樣內存中就出現了兩份System字節碼。
- 如果使用委託機制,會遞歸的向父類查找,也就是首選用Bootstrap嘗試加載,如果找不到再向下。這裏的System就能在Bootstrap中找到然後加載,如果此時類B也要加載System,也從Bootstrap開始,此時Bootstrap發現已經加載過了System那麼直接返回內存中的System即可而不需要重新加載,這樣內存中就只有一份System的字節碼了。
當Java虛擬機要加載一個類時,到底派出哪個類加載器去加載呢?
- 首先當前線程的類加載器去加載線程中的第一個類(假設爲類A)。
注:當前線程的類加載器可以通過Thread類的getContextClassLoader()獲得,也可以通過setContextClassLoader()自己設置類加載器。 - 如果類A中引用了類B,Java虛擬機將使用加載類A的類加載器去加載類B。
- 還可以直接調用
ClassLoader.loadClass()
方法來指定某個類加載器去加載某個類。
如果定義自己的符合雙親委派模型的加載器類?
繼承java.lang.ClassLoader,並重新findClass方法,以指定如何找到並加載class字節流,最早的自定義加載器是通過重載loadClass實現的,爲了符合雙親委派模型,一般不建議再重寫此方法。
獲取加載器的方法總結
獲得系統類加載器
public class testClassLoader { @Test public void test(){ //application class loader System.out.println(ClassLoader.getSystemClassLoader()); //extensions class loader System.out.println(ClassLoader.getSystemClassLoader().getParent()); //bootstrap class loader System.out.println(ClassLoader.getSystemClassLoader().getParent().getParent()); } }
輸出爲:
可以看出ClassLoader類是由AppClassLoader加載的。他的父親是ExtClassLoader,ExtClassLoader的父親無法獲取是因爲它是用C++實現的。
獲得指定類的加載器
Lotto.class.getClassLoader();
加載器常用方法總結
方法 | 說明 |
---|---|
getParent() | 返回該類加載器的父類加載器 |
loadClass(String name) |
加載名稱爲name的類,返回結果是一個java.lang.Class 實例 |
findClass(String name) |
查找名稱爲name的類,返回結果是一個java.lang.Class 實例 |
findLoadedClass(String name) |
查找名稱爲name的已經被加載的類,返回結果是一個java.lang.Class 實例 |
defineClass(String name, byte[] b, int off, int len) |
把字節數組b中的內容轉換爲一個java.lang.Class 實例,該方法是final的 |
resolveClass(Class<?> c) | 鏈接指定的class |
一般來說,如果需要實現自己的類加載器,只需要實現findClass
方法即可,loadClass
封裝了下面提到的委託機制。