類加載器實現了經過一個類的全限定名來獲取此類的二進制字節流,這個動做是放到Java虛擬機外部實現的,以便讓應用程序本身決定如何去獲取所須要的實現類。java
關於類 的惟一性:只有加載它的類加載器和類自己一同確立其在Java虛擬機中的惟一性,即便這兩個類來源於同一個Class文件,被同一個虛擬機加載,只要它們的類加載器不一樣,那這兩個類就一定不一樣bootstrap
public class ClassLoadTest {
public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
ClassLoader classLoader = new ClassLoader() {
@Override
public Class<?> loadClass(String name) throws ClassNotFoundException {
try {
String fileName = name.substring(name.lastIndexOf(".") + 1) + ".class";
InputStream inputStream = getClass().getResourceAsStream(fileName);
if (inputStream == null) {
return super.loadClass(name);
}
byte[] b = new byte[inputStream.available()];
inputStream.read(b);
return defineClass(name,b,0,b.length);
} catch (IOException e) {
e.printStackTrace();
throw new ClassNotFoundException(name);
}
}
};
Object obj = classLoader.loadClass("chap07.ClassLoadTest").newInstance();
System.out.println(obj.getClass());
System.out.println(obj instanceof chap07.ClassLoadTest);
}
}
複製代碼
運行結果: ide
從第一行看出,這個對象確實是類chap07.ClassLoaderTest實例化出來的對象,可是從第二句能夠發現,這個對象與類chap07.ClassLoaderTest作所屬類型檢查的時候卻返回了false,這是由於虛擬機中存在了兩個ClassLoader,一個是由系統 應用程序類加載器加載的,另一個是由咱們自定義加載器加載的,雖然都來自同一個Class文件,但依然是兩個獨立的類。從Java虛擬機的角度講,只存在兩種不一樣的類加載器:一種是啓動類加載器(Bootstrap ClassLoader),這個類加載器使用C++語言實現,是虛擬機自身的一部分。另外一種就是其餘的類加載器,這些類加載器由Java語言實現,獨立於虛擬機外部,而且所有繼承自抽象類java.lang.ClassLoader。this
從Java開發人員角度看,絕大部分Java程序都會使用到如下3中系統提供的類加載器。spa
咱們的應用程序都是由這3種類加載器互相配合進行加載的,若是有必要,還能夠加入本身定義的類加載器。這些類加載器之間的關係通常以下圖所示:code
這種層次關係稱爲雙親委派模型(Parents Delegation Model)。雙親委派模型要求除了頂層的啓動類加載器外,其他的類加載器都應當有本身的父類加載器。這裏類加載器之間的父子關係通常不會以繼承(Inheritance)的關係來實現,而是都使用組合(Composition)關係來複用父加載器的代碼。cdn
雙親委派模型的工做過程是:若是一個類加載器收到了類加載的請求,它首先不會本身去嘗試加載這個類,而是把這個請求委派給父類加載器去完成,每個層次的類加載器都是如此,所以全部的加載請求最終都應該傳送到頂層的啓動類加載器中,只有當父加載器反饋本身沒法完成這個加載請求(它的搜索範圍中沒有找到所需的類)時,子加載器纔會嘗試本身去加載。對象
使用雙親委派模型來組織類加載器之間的關係,有一個顯而易見的好處就是Java類隨着它的類加載器一塊兒具有了一種帶有優先級的層次關係。 例如類java.lang.Object,它存放在rt.jar之中,不管哪個類加載器要加載這個類,最終都是委派給處於模型最頂端的啓動類加載器進行加載,所以Object類在程序的各類類加載器環境中都是同一個類。相反,若是沒有使用雙親委派模型,由各個類加載器自行去加載的話,若是用戶本身編寫了一個稱爲java.lang.Object的類,並放在程序的ClassPath中,那系統中將會出現多個不一樣的Object類,Java類型體系中最基礎的行爲也就沒法保證,應用程序也將會變得一片混亂。若是讀者有興趣的話,能夠嘗試去編寫一個與rt.jar類庫中已有類重名的Java類,將會發現能夠正常編譯,但永遠沒法被加載運行。blog
雙親委派的代碼都幾種在java.lang.ClassLoader的loadClass()方法之中繼承
protected Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException
{
synchronized (getClassLoadingLock(name)) {
// 首先,檢查請求的類是否已經被加載過了
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
// 說明父類加載器沒法完成加載請求
}
if (c == null) {
// 調用自己的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;
}
}
複製代碼