1. ClassLoader的繼承關(guān)系
ClassLoader是什么鬼?為什么我們要如此大費(fèi)周章的講解這個(gè)?
還記得AppClassLoader、ExtClassLoader么?他們與ClassLoader之間的關(guān)系是什么?

ClassLoader繼承關(guān)系

AppClassLoader+ExtClassLoader

URLClassLoader

SecureClassLoader

ClassLoader
2. ClassLoader重要方法loadclass()代碼解讀。
直接上代碼,代碼上注釋有說(shuō)明。
```
public Class<?> loadClass(String name) throws ClassNotFoundException {
return loadClass(name, false);
}
protected Class<?> loadClass(String name, boolean resolve) //resolve字段表示是否進(jìn)行【連接】階段處理
throws ClassNotFoundException
{
synchronized (getClassLoadingLock(name)) {
// First, check if the class has already been loaded
// 首先,判斷該類是否已經(jīng)加載過(guò)了。
Class<?> c = findLoadedClass(name);
if (c == null) {
long t0 = System.nanoTime();
try {
if (parent != null) { //如果父類存在
// 如果未加載過(guò),則委派給父類進(jìn)行加載。
c = parent.loadClass(name, false);
} else {
// 如果父類不存在,則交給BootstrapClassLoader來(lái)加載。 什么時(shí)候父類不存在呢?其實(shí)就是ExtClassLoader不存在父類的情況。
c = findBootstrapClassOrNull(name);
}
} catch (ClassNotFoundException e) {
// ClassNotFoundException thrown if class not found
// from the non-null parent class loader
// 如果父類通過(guò)緩存+加載都無(wú)法找到,并拋出ClassNotFoundException異常時(shí),則捕獲異常但不處理。
}
if (c == null) {
// If still not found, then invoke findClass in order
// to find the class.
long t1 = System.nanoTime();
// 如果委托的父類們都無(wú)法找到該類,則本加載器自己親自動(dòng)手去查找。
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;
}
}
```
代碼中有幾個(gè)關(guān)鍵調(diào)用需要注意:
① Class<?> c = findLoadedClass(name)通過(guò)緩存查找判斷是否存在該類。
進(jìn)一步查看該方法實(shí)現(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);
```
② 當(dāng)parent != null時(shí),c = parent.loadClass(name, false);。如果父類不為空,則委派給父類的loadClass()方法執(zhí)行。
當(dāng) parent == null是,```c = findBootstrapClassOrNull(name);```父類如果為空時(shí),則委派給BootstrapClassLoader來(lái)查找。
這里就是雙親委派模型出現(xiàn)了。
③ 當(dāng)在經(jīng)過(guò)父類們緩存查找和加載后,仍然未找到該類,則本加載器會(huì)親自進(jìn)行查找c = findClass(name);。這個(gè)方法很關(guān)鍵。
```
protected Class<?> findClass(String name) throws ClassNotFoundException {
throw new ClassNotFoundException(name);
}
```
3. 雙親委派模型的驗(yàn)證
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)點(diǎn)
這里補(bǔ)充下幾個(gè)雙親委派模型的特點(diǎn)。
- 系統(tǒng)類防止內(nèi)存中出現(xiàn)多份同樣的字節(jié)碼
因?yàn)镴ava類隨著它的類加載器一起具備了一種帶有優(yōu)先級(jí)的層次關(guān)系。雙親委派模型很好的解決了各個(gè)類加載器的基礎(chǔ)類的統(tǒng)一問(wèn)題(越基礎(chǔ)的類由越上層的加載器進(jìn)行加載)。 - 保證Java程序安全穩(wěn)定運(yùn)行
使用雙親委派模型來(lái)組織類加載器之間的關(guān)系,有一個(gè)顯而易見(jiàn)的好處就是Java類隨著它的類加載器一起具備了一種帶有優(yōu)先級(jí)的層次關(guān)系。例如,類java.lang.Object,它存放在rt.jar中,無(wú)論哪一個(gè)類加載器要加載這個(gè)了類,最終都是委派給模型最頂端的啟動(dòng)類加載器進(jìn)行加載,因此Object類在程序的各個(gè)類加載器環(huán)境中都是同一個(gè)類。
相反,如果沒(méi)有使用雙親委派模型,由各個(gè)類加載器自行去加載的話,如果用戶自己編寫了一個(gè)稱為java.lang.Object的類,并放在程序的ClassPath中,那系統(tǒng)中將會(huì)出現(xiàn)多個(gè)不同的Object類,Java類型體系中最基本的行為也就無(wú)法保證,應(yīng)用程序也會(huì)變得一片混亂。