類載入的雙親委派模型
雙親委派模型要求除了頂層的啟動類載入器外,其他的類載入器都應當有自己的父類載入器。這裡類載入器之間的父子關係一般不會以繼承關係來實現,而是都使用組合關係來複用父載入器的程式碼
工作過程:
如果一個類載入器收到了類載入的請求,它首先不會自己去嘗試載入這個類,而是把這個請求委派給父類載入器去完成,每一個層次的類載入器都是如此,因此所有的載入請求最終都應該傳遞到頂層的啟動類載入器中,只有當父類載入器反饋自己無法完成這個請求(它的搜尋範圍中沒有找到所需的類)時,子載入器才會嘗試自己去載入
好處:
Java類隨著它的類載入器一起具備了一種帶有優先順序的層次關係。例如類Object,它放在rt.jar中,無論哪一個類載入器要載入這個類,最終都是委派給啟動類載入器進行載入,因此Object類在程式的各種類載入器環境中都是同一個類。
判斷兩個類是否相同是通過classloader.class這種方式進行的,所以哪怕是同一個class檔案如果被兩個classloader載入,那麼他們也是不同的類
類載入器按照層次,從頂層到底層,分為以下三種:
1.啟動類載入器(Bootstrap ClassLoader)
這個類載入器負責將存放在JAVA_HOME/lib下的,或者被-Xbootclasspath引數所指定的路徑中的,並且是虛擬機器識別的類庫載入到虛擬機器記憶體中。啟動類載入器無法被Java程式直接引用。
2.擴充套件類載入器(Extension ClassLoader)
這個載入器負責載入JAVA_HOME/lib/ext目錄中的,或者被java.ext.dirs系統變數所指定的路徑中的所有類庫,開發者可以直接使用擴充套件類載入器
3.應用程式類載入器(Application ClassLoader)
這個載入器是ClassLoader中getSystemClassLoader()方法的返回值,所以一般也稱它為系統類載入器。它負責載入使用者類路徑(Classpath)上所指定的類庫,可直接使用這個載入器,如果應用程式沒有自定義自己的類載入器,一般情況下這個就是程式中預設的類載入器。
關於這部分的問題,可以直接閱讀sun.misc.Launcher的原始碼,或者看末尾連結給出的文章。
實現自己的載入器
只需要繼承ClassLoader,並覆蓋findClass方法。在呼叫loadClass方法時,會先根據委派模型在父載入器中載入,如果載入失敗,則會呼叫自己的findClass方法來完成載入。
類載入器如何識別
通過findLoadedClass來識別是否已經載入某個類,這個方法是findLoadedClass0的一個包裝類,而findLoadedClass0是一個native方法
顯式載入類
在程式碼中顯式載入某個類,有三種方法:
1. this.getClass().getClassLoader().loadClass()
2. Class.forName()
3. MyClassLoader.findClass()複製程式碼
在大多數情況下,並不需要自己實現一個類載入器,而是直接採用URLClassLoader來載入外部Class或者jar檔案。JDK中的AppClassloader也是繼承的URLClassLoader:
public class Test{
public static void main(String[] args) {
URL url = null;
try {
url = new File("/tmp/Test.class").toURI().toURL();
ClassLoader loader = new URLClassLoader(urls);
classLoaderList.add(loader);
loader.loadClass("com.paddx.test.memory.Test");
} catch (Exception e) {
e.printStackTrace();
}
}
}複製程式碼
ClassLoader.loadClass()的載入步驟:
1. 呼叫 findLoadedClass(String) 來檢查是否已經載入類。
2. 在父類載入器上呼叫 loadClass 方法。如果父類載入器為 null,則使用虛擬機器的內建類載入器。
3. 呼叫 findClass(String) 方法查詢類。複製程式碼
ClassLoader是一個抽象類。Java 預設實現是AppClassLoader。 從原始碼可以看出來findClass和loadClass的不同之處:findClass只是loadClass的其中一步,如果父載入器和根載入器都沒有找到這個類,就會呼叫findClass方法。如果繼承了findClass方法,那麼雙親委派機制就不會被破壞。這其實是一個模板方法模式。
protected Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException
{
synchronized (getClassLoadingLock(name)) {
// First, check if the class has already been loaded
Class c = findLoadedClass(name);
if (c == null) {
long t0 = System.nanoTime();
try {
if (parent != null) {
c = parent.loadClass(name, false);
} else {
c = findBootstrapClassOrNull(name);
}
} catch (ClassNotFoundException e) {
// ClassNotFoundException thrown if class not found
// from the non-null parent class loader
}
if (c == null) {
// If still not found, then invoke findClass in order
// to find the class.
long t1 = System.nanoTime();
c = findClass(name);
// this is the defining class loader; record the stats
sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
sun.misc.PerfCounter.getFindClasses().increment();
}
}
if (resolve) {
resolveClass(c);
}
return c;
}
}複製程式碼
由於抽象類的預設findClass實現是:
protected Class<?> findClass(String name) throws ClassNotFoundException {
throw new ClassNotFoundException(name);
}複製程式碼
所以子類必須實現這個方法。
下面這個文章講的挺好:
blog.csdn.net/jiangwei091…