在jvm中的類基本上都是由類加載器來加載的(像基本數(shù)據(jù)類型疫诽,數(shù)組類型這些不是類加載器加載的,是由jvm自己加載的)胯努。并且對(duì)于java中的一個(gè)類是由加載它的類加載器和這個(gè)類本身所 唯一確定的贪磺。也就是說如果兩個(gè)類是來源同一個(gè)Class文件但沒有被同一個(gè)類加載器加載拧略,那么這兩個(gè)類就不是相等的。
這里面說的“相等”李破,是類的Class對(duì)象的equals()方法宠哄、isAssignableForm()方法、isInstance()方法返回的結(jié)果嗤攻,也包括使用instanceof這個(gè)關(guān)鍵字所判定的情況毛嫉。
雙親委派模型
從使用Java的程序員來說,Java提供的類加載器可以分為這3種:
- 啟動(dòng)類加載器(Bootstrap ClassLoader):這個(gè)類加載器在HotSpot虛擬機(jī)中是由C++實(shí)現(xiàn)的妇菱。主要負(fù)責(zé)將<JAVA_HOME>\lib 目錄下的承粤,或者是-Xbootclasspath參數(shù)所指定的路徑中的,并且是虛擬機(jī)按照文件名識(shí)別的恶耽,也就是說只會(huì)識(shí)別虛擬機(jī)事先定義好的幾個(gè)包密任,你把第三方或者是自己打的jar包放再目錄下也不會(huì)被這個(gè)類加載器加載。
用Class.getClassLoader()方法來獲取類的加載器的時(shí)候偷俭,如果這個(gè)類是Bootstrap ClassLoader加載的那么則會(huì)返回null浪讳。
代碼如下:
/**
* Returns the class loader for the class. Some implementations may use
* null to represent the bootstrap class loader. This method will return
* null in such implementations if this class was loaded by the bootstrap
* class loader.
*
*/
@CallerSensitive
public ClassLoader getClassLoader() {
ClassLoader cl = getClassLoader0();
if (cl == null)
return null;
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
ClassLoader.checkClassLoaderPermission(cl, Reflection.getCallerClass());
}
return cl;
}
- 擴(kuò)展類加載器(Extension ClassLoader):這個(gè)類加載器是sun.misc.Launcher$ExtClassLoader來試下的,它負(fù)責(zé)加載<JAVA_HOME>\lib\ext目錄下類庫(kù)涌萤。我們可以直接在代碼中使用這個(gè)類加載器淹遵。
- 應(yīng)用程序類加載器(Application ClassLoader):這個(gè)類加載器是sun.misc.Launcher$AppClassLoader實(shí)現(xiàn)的口猜。這個(gè)類加載器是ClassLoader類中的getSystemClassLoader()方法返回的。所以我們一般稱之為類加載器透揣。它負(fù)責(zé)加載用戶類路徑(就是ClassPath)上所指定的目錄济炎。我們可以直接使用這個(gè)類加載器,如果我們沒有自定義類加載器一般情況下我們可以使用這個(gè)加載器辐真。
我們也可以自己定義類加載器须尚,這些類加載器的關(guān)系如下圖:
上圖所示的這種層次關(guān)系我們一般稱之為雙親委派模型(Parents Delegation Model)。雙親委派模型要求除了頂層的啟動(dòng)類加載器之外侍咱,其他的類加載都應(yīng)當(dāng)有自己的父類加載器耐床。這里類加載器之間的父子關(guān)系一般不會(huì)以繼承的方式實(shí)現(xiàn),而是使用組合的方式復(fù)用父類加載器的代碼楔脯。
雙親委派模式的工作過程:
如果一個(gè)類加載收到了類加載的請(qǐng)求撩轰,它不會(huì)自己去嘗試加載這個(gè)類,而是把這個(gè)請(qǐng)求委派給父類加載器去完成昧廷,每個(gè)層次都是這樣堪嫂,因此所欲的加載請(qǐng)求在這個(gè)模式下都會(huì)傳送到頂層的啟動(dòng)類加載器中去。只有當(dāng)父加載器加載不了這個(gè)類的時(shí)候子類加載器才會(huì)去嘗試加載木柬。
雖然這種雙親委派模型雖然是Java推薦的一種實(shí)現(xiàn)方式皆串,但是它不是強(qiáng)制的。也就是說我們可以自己定義一個(gè)加載器不用去把它交給父加載器弄诲。
使用這種雙親委派模型來進(jìn)行類加載有什么好處呢愚战?首先,它讓類加載器具有了一種帶有優(yōu)先級(jí)的層次關(guān)系齐遵。比如類java.lang.Object,它存放在rt.jar中寂玲,但是無論哪一個(gè)類加載器要加載這個(gè)類,最終都會(huì)到啟動(dòng)類加載器上進(jìn)行加載梗摇,所以O(shè)bject類在這種雙親委派模型下都是同一個(gè)類拓哟。如果沒有使用這個(gè)模型,那么我們自己編寫了一個(gè)java.lang.Object伶授,并放在程序的ClassPath中断序,那系統(tǒng)中將會(huì)出現(xiàn)多個(gè)不同的Object類,那么程序就會(huì)很很亂糜烹,正確性也無法得到保證违诗。
那如何實(shí)現(xiàn)這個(gè)雙親委派模型呢?其實(shí)java里實(shí)現(xiàn)是非常簡(jiǎn)單的疮蹦。我們只需要繼承ClassLoader后诸迟,重新findClass方法即可。具體的ClassLoader里的loadClass()方法如下:
/**
* Loads the class with the specified <a href="#name">binary name</a>. The
* default implementation of this method searches for classes in the
* following order:
*
* @param name
* The <a href="#name">binary name</a> of the class
*
* @param resolve
* If <tt>true</tt> then resolve the class
*
* @return The resulting <tt>Class</tt> object
*
* @throws ClassNotFoundException
* If the class could not be found
*/
protected Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException
{
synchronized (getClassLoadingLock(name)) {
// 首先,檢查請(qǐng)求的類是否已經(jīng)被加載過了
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
// 說明父類無法加載
}
if (c == null) {
// If still not found, then invoke findClass in order
// 調(diào)用本身的findClass去加載
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;
}
}