1. ClassLoader的繼承關(guān)系
ClassLoader是什么鬼?為什么我們要如此大費周章的講解這個虎谢?
還記得AppClassLoader、ExtClassLoader么曹质?他們與ClassLoader之間的關(guān)系是什么婴噩?
2. ClassLoader重要方法loadclass()代碼解讀。
直接上代碼羽德,代碼上注釋有說明几莽。
```
public Class<?> loadClass(String name) throws ClassNotFoundException {
return loadClass(name, false);
}
protected Class<?> loadClass(String name, boolean resolve) //resolve字段表示是否進行【連接】階段處理
throws ClassNotFoundException
{
synchronized (getClassLoadingLock(name)) {
// First, check if the class has already been loaded
// 首先,判斷該類是否已經(jīng)加載過了宅静。
Class<?> c = findLoadedClass(name);
if (c == null) {
long t0 = System.nanoTime();
try {
if (parent != null) { //如果父類存在
// 如果未加載過章蚣,則委派給父類進行加載。
c = parent.loadClass(name, false);
} else {
// 如果父類不存在姨夹,則交給BootstrapClassLoader來加載纤垂。 什么時候父類不存在呢矾策?其實就是ExtClassLoader不存在父類的情況。
c = findBootstrapClassOrNull(name);
}
} catch (ClassNotFoundException e) {
// ClassNotFoundException thrown if class not found
// from the non-null parent class loader
// 如果父類通過緩存+加載都無法找到洒忧,并拋出ClassNotFoundException異常時蝴韭,則捕獲異常但不處理。
}
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;
}
}
```
代碼中有幾個關(guān)鍵調(diào)用需要注意:
① Class<?> c = findLoadedClass(name)
通過緩存查找判斷是否存在該類。
進一步查看該方法實現(xiàn)蛉抓,又調(diào)用了native findLoadedClass0方法庆尘。
```
protected final Class<?> findLoadedClass(String name) {
if (!checkName(name))
return null;
return findLoadedClass0(name);
}
private native final Class<?> findLoadedClass0(String name);
```
② 當parent != null時,c = parent.loadClass(name, false);
巷送。如果父類不為空驶忌,則委派給父類的loadClass()方法執(zhí)行。
當 parent == null是笑跛,```c = findBootstrapClassOrNull(name);```父類如果為空時付魔,則委派給BootstrapClassLoader來查找。
這里就是雙親委派模型出現(xiàn)了飞蹂。
③ 當在經(jīng)過父類們緩存查找和加載后几苍,仍然未找到該類,則本加載器會親自進行查找c = findClass(name);
陈哑。這個方法很關(guān)鍵妻坝。
```
protected Class<?> findClass(String name) throws ClassNotFoundException {
throw new ClassNotFoundException(name);
}
```
3. 雙親委派模型的驗證
public static void main(String[] args) {
ClassLoader loader = TestStatic3.class.getClassLoader();
System.out.println(loader);
System.out.println(loader.getParent());
System.out.println(loader.getParent().getParent());
}
輸出結(jié)果:
sun.misc.Launcher$AppClassLoader@b4aac2
sun.misc.Launcher$ExtClassLoader@193b845
null
4. 雙親委派模型的優(yōu)點
這里補充下幾個雙親委派模型的特點。
- 系統(tǒng)類防止內(nèi)存中出現(xiàn)多份同樣的字節(jié)碼
因為Java類隨著它的類加載器一起具備了一種帶有優(yōu)先級的層次關(guān)系惊窖。雙親委派模型很好的解決了各個類加載器的基礎(chǔ)類的統(tǒng)一問題(越基礎(chǔ)的類由越上層的加載器進行加載)刽宪。 - 保證Java程序安全穩(wěn)定運行
使用雙親委派模型來組織類加載器之間的關(guān)系,有一個顯而易見的好處就是Java類隨著它的類加載器一起具備了一種帶有優(yōu)先級的層次關(guān)系界酒。例如圣拄,類java.lang.Object,它存放在rt.jar中毁欣,無論哪一個類加載器要加載這個了類售担,最終都是委派給模型最頂端的啟動類加載器進行加載,因此Object類在程序的各個類加載器環(huán)境中都是同一個類署辉。
相反,如果沒有使用雙親委派模型岩四,由各個類加載器自行去加載的話哭尝,如果用戶自己編寫了一個稱為java.lang.Object的類,并放在程序的ClassPath中剖煌,那系統(tǒng)中將會出現(xiàn)多個不同的Object類材鹦,Java類型體系中最基本的行為也就無法保證逝淹,應用程序也會變得一片混亂。