雙親委派模型要求除了頂層的啓動類加載器外,其餘的類加載器都應當有本身的父類加載器。這裏類加載器之間的父子關係通常不會以繼承關係來實現,而是都使用組合關係來複用父加載器的代碼javascript
若是一個類加載器收到了類加載的請求,它首先不會本身去嘗試加載這個類,而是把這個請求委派給父類加載器去完成,每個層次的類加載器都是如此,所以全部的加載請求最終都應該傳遞到頂層的啓動類加載器中,只有當父類加載器反饋本身沒法完成這個請求(它的搜索範圍中沒有找到所需的類)時,子加載器纔會嘗試本身去加載java
Java類隨着它的類加載器一塊兒具有了一種帶有優先級的層次關係。例如類Object,它放在rt.jar中,不管哪個類加載器要加載這個類,最終都是委派給啓動類加載器進行加載,所以Object類在程序的各類類加載器環境中都是同一個類。
判斷兩個類是否相同是經過classloader.class這種方式進行的,因此哪怕是同一個class文件若是被兩個classloader加載,那麼他們也是不一樣的類
bootstrap
這個類加載器負責將存放在JAVA_HOME/lib下的,或者被-Xbootclasspath參數所指定的路徑中的,而且是虛擬機識別的類庫加載到虛擬機內存中。啓動類加載器沒法被Java程序直接引用。app
這個加載器負責加載JAVA_HOME/lib/ext目錄中的,或者被java.ext.dirs系統變量所指定的路徑中的全部類庫,開發者能夠直接使用擴展類加載器ui
這個加載器是ClassLoader中getSystemClassLoader()方法的返回值,因此通常也稱它爲系統類加載器。它負責加載用戶類路徑(Classpath)上所指定的類庫,可直接使用這個加載器,若是應用程序沒有自定義本身的類加載器,通常狀況下這個就是程序中默認的類加載器。this
關於這部分的問題,能夠直接閱讀sun.misc.Launcher的源碼,或者看末尾連接給出的文章。url
只須要繼承ClassLoader,並覆蓋findClass方法。在調用loadClass方法時,會先根據委派模型在父加載器中加載,若是加載失敗,則會調用本身的findClass方法來完成加載。spa
經過findLoadedClass來識別是否已經加載某個類,這個方法是findLoadedClass0的一個包裝類,而findLoadedClass0是一個native方法.net
在代碼中顯式加載某個類,有三種方法:code
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();
}
}
}複製代碼
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…