一個java程序被執(zhí)行,編譯器先將.java文件編譯成class文件,然后classloader將class文件加載到j(luò)vm內(nèi)存中來執(zhí)行應(yīng)用程序瓢捉。classloader起到了至關(guān)重要的作用,下面我們來看下classloader的分類:
系統(tǒng)類加載器
- Bootstrap ClassLoader
用C/C++代碼實現(xiàn)的加載器,用于加載Java虛擬機運行時所需要的系統(tǒng)類弟塞,如java.lang.、java.uti.等這些系統(tǒng)類拙已,它們默認在$JAVA_HOME/jre/lib目錄中决记,也可以通過啟動Java虛擬機時指定-Xbootclasspath選項,來改變Bootstrap ClassLoader的加載目錄倍踪。
Java虛擬機的啟動就是通過 Bootstrap ClassLoader創(chuàng)建一個初始類來完成的系宫。由于Bootstrap ClassLoader是使用C/C++語言實現(xiàn)的索昂, 所以該加載器不能被Java代碼訪問到。需要注意的是Bootstrap ClassLoader并不繼承java.lang.ClassLoader扩借。 - Extensions ClassLoader
用于加載 Java 的拓展類 椒惨,拓展類的jar包一般會放在$JAVA_HOME/jre/lib/ext目錄下,用來提供除了系統(tǒng)類之外的額外功能潮罪。也可以通過-Djava.ext.dirs選項添加和修改Extensions ClassLoader加載的路徑康谆。 - App ClassLoader
負責(zé)加載當(dāng)前應(yīng)用程序Classpath目錄下的所有jar和Class文件。也可以加載通過-Djava.class.path選項所指定的目錄下的jar和Class文件嫉到。
自定義類加載器
就是我們自己寫一個類繼承ClassLoader沃暗,然后重寫findClass方法加載指定目錄下的class文件
classLoader繼承關(guān)系
我們知道系統(tǒng)所提供的類加載器有3種類型,但是系統(tǒng)提供的ClassLoader相關(guān)類卻不只3個何恶。另外描睦,AppClassLoader的父類加載器為ExtClassLoader,并不代表AppClassLoader繼承自ExtClassLoader导而,ClassLoader的繼承關(guān)系如下所示忱叭。
image.png
可以看到上圖中共有5個ClassLoader相關(guān)類,下面簡單對它們進行介紹:
- ClassLoader是一個抽象類今艺,其中定義了ClassLoader的主要功能韵丑。
- SecureClassLoader繼承了抽象類ClassLoader,但SecureClassLoader并不是ClassLoader的實現(xiàn)類虚缎,而是拓展了ClassLoader類加入了權(quán)限方面的功能撵彻,加強了ClassLoader的安全性。
- URLClassLoader繼承自SecureClassLoader实牡,用來通過URl路徑從jar文件和文件夾中加載類和資源陌僵。
- ExtClassLoader和AppClassLoader都繼承自URLClassLoader,它們都是Launcher 的內(nèi)部類创坞,Launcher 是Java虛擬機的入口應(yīng)用碗短,ExtClassLoader和AppClassLoader都是在Launcher中進行初始化的。
雙親委派機制
類加載器查找Class所采用的是雙親委托模式题涨,所謂雙親委托模式就是首先判斷該Class是否已經(jīng)加載偎谁,如果沒有則不是自身去查找而是委托給父加載器進行查找,這樣依次的進行遞歸纲堵,直到委托到最頂層的Bootstrap ClassLoader巡雨,如果Bootstrap ClassLoader找到了該Class,就會直接返回席函,如果沒找到铐望,則繼續(xù)依次向下查找,如果還沒找到則最后會交由自身去查找。
image.png
這樣講可能會有些抽象正蛙,來直接看源碼:
protected Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException
{
// First, check if the class has already been loaded
Class<?> c = findLoadedClass(name);//第一步
if (c == null) {
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.
c = findClass(name);
}
}
return c;
}
protected final Class<?> findLoadedClass(String name) {
ClassLoader loader;
if (this == BootClassLoader.getInstance())
loader = null;
else
loader = this;
return VMClassLoader.findLoadedClass(loader, name);
}
- findLoadedClass 在當(dāng)前classloader中查找是否加載過該class炕舵,如果沒加載過就交給父加載器進行加載
- parent.loadClass(name, false),父加載器存在的話就交給父加載器進行加載跟畅,不存在的就直接交由系統(tǒng)跟類加載器家在咽筋。同樣父加載器也是在當(dāng)前的classloader中查找是否加載過該class,如果沒加載過就交給父加載器進行加載徊件。不斷重復(fù)第一步奸攻,遞歸調(diào)用。
- 一直委托到Bootstrap ClassLoader虱痕,如果Bootstrap ClassLoader在緩存中還沒有查找到Class文件睹耐,則在自己的規(guī)定路徑$JAVA_HOME/jre/libr中或者-Xbootclasspath選項指定路徑的jar包中進行查找,如果找到則返回該Class部翘,如果沒有則交給子加載器Extensions ClassLoader硝训。
- Extensions ClassLoader查找$JAVA_HOME/jre/lib/ext目錄下或者-Djava.ext.dirs選項指定目錄下的jar包,如果找到就返回新思,找不到則交給App ClassLoader窖梁。
- App ClassLoade查找Classpath目錄下或者-Djava.class.path選項所指定的目錄下的jar包和Class文件,如果找到就返回夹囚,找不到交給我們自定義的類加載器纵刘,如果還找不到則拋出異常
兩個過程,先從下到上查詢是否有類加載器加載過該class荸哟,然后從上到下詢問類加載器是否可以加載該class
雙親委托模式的好處
采取雙親委托模式主要有兩點好處:
- 避免重復(fù)加載假哎,如果已經(jīng)加載過一次Class,就不需要再次加載鞍历,而是先從緩存中直接讀取舵抹。
- 更加安全,如果不使用雙親委托模式劣砍,就可以自定義一個String類來替代系統(tǒng)的String類惧蛹,這顯然會造成安全隱患,采用雙親委托模式會使得系統(tǒng)的String類在Java虛擬機啟動時就被加載秆剪,也就無法自定義String類來替代系統(tǒng)的String類赊淑,除非我們修改
類加載器搜索類的默認算法。還有一點仅讽,只有兩個類名一致并且被同一個類加載器加載的類,Java虛擬機才會認為它們是同一個類钾挟,想要騙過Java虛擬機顯然不會那么容易
- 更加安全,如果不使用雙親委托模式劣砍,就可以自定義一個String類來替代系統(tǒng)的String類惧蛹,這顯然會造成安全隱患,采用雙親委托模式會使得系統(tǒng)的String類在Java虛擬機啟動時就被加載秆剪,也就無法自定義String類來替代系統(tǒng)的String類赊淑,除非我們修改