類(lèi)加載
Java類(lèi)通過(guò)編譯生成對(duì)應(yīng).class文件谱姓,JVM根據(jù)實(shí)際情況把當(dāng)前需要的類(lèi)從.class動(dòng)態(tài)加載到內(nèi)存創(chuàng)建實(shí)例漂羊,ClassLoader就負(fù)責(zé)完成這個(gè)加載任務(wù)后控。有了ClassLoader匿级,Java運(yùn)行時(shí)系統(tǒng)不需要知道文件與文件系統(tǒng)的設(shè)置狂窑。
正是因?yàn)镴ava類(lèi)必須由某個(gè)類(lèi)加載器裝入到內(nèi)存媳板,我們也可以在運(yùn)行時(shí)才指定需要的類(lèi)文件。
Java中的三個(gè)默認(rèn)類(lèi)加載器
除了Bootstrap ClassLoader泉哈,每個(gè)類(lèi)裝載器都有一個(gè)父裝載器(parent class loader)蛉幸,且ExtClassLoader和AppClassLoader均繼承ClassLoader類(lèi)。
引導(dǎo)(Bootstrap)類(lèi)加載器丛晦。由原生代碼(如C語(yǔ)言)編寫(xiě)奕纫,不繼承自
java.lang.ClassLoader
。負(fù)責(zé)加載存儲(chǔ)在<JAVA_HOME>/jre/lib
目錄中的核心Java庫(kù)烫沙。擴(kuò)展(Extensions)類(lèi)加載器匹层。用來(lái)在
<JAVA_HOME>/jre/lib/ext
或java.ext.dirs
指明的目錄中加載 Java擴(kuò)展庫(kù),Java 虛擬機(jī)的實(shí)現(xiàn)會(huì)提供一個(gè)擴(kuò)展庫(kù)目錄锌蓄。該類(lèi)加載器在此目錄里面查找并加載 Java 類(lèi)升筏。該類(lèi)由sun.misc.Launcher$ExtClassLoader
實(shí)現(xiàn)。應(yīng)用(Application)類(lèi)加載器瘸爽。根據(jù) Java應(yīng)用程序的類(lèi)路徑
(java.class.path或CLASSPATH環(huán)境變量)
來(lái)加載 Java 類(lèi)您访。一般來(lái)說(shuō),Java 應(yīng)用的類(lèi)都是由它來(lái)完成加載的剪决,可以通過(guò)ClassLoader.getSystemClassLoader()
來(lái)獲取灵汪。該類(lèi)由sun.misc.Launcher$AppClassLoader實(shí)現(xiàn)
。
通過(guò)Bootstrap ClassLoader加載的庫(kù)
URL[] urls = sun.misc.Launcher.getBootstrapClassPath().getURLs();
for (URL url : urls) {
System.out.println(url.toExternalForm());
}
結(jié)果
file:/Library/Java/JavaVirtualMachines/jdk1.8.0_101.jdk/Contents/Home/jre/lib/resources.jar
file:/Library/Java/JavaVirtualMachines/jdk1.8.0_101.jdk/Contents/Home/jre/lib/rt.jar
file:/Library/Java/JavaVirtualMachines/jdk1.8.0_101.jdk/Contents/Home/jre/lib/sunrsasign.jar
file:/Library/Java/JavaVirtualMachines/jdk1.8.0_101.jdk/Contents/Home/jre/lib/jsse.jar
file:/Library/Java/JavaVirtualMachines/jdk1.8.0_101.jdk/Contents/Home/jre/lib/jce.jar
file:/Library/Java/JavaVirtualMachines/jdk1.8.0_101.jdk/Contents/Home/jre/lib/charsets.jar
file:/Library/Java/JavaVirtualMachines/jdk1.8.0_101.jdk/Contents/Home/jre/lib/jfr.jar
file:/Library/Java/JavaVirtualMachines/jdk1.8.0_101.jdk/Contents/Home/jre/classes
ClassLoader類(lèi)加載
ClassLoader通過(guò)雙親委托的方式來(lái)搜索類(lèi)柑潦,而雙親委托是一種委派思想享言。
當(dāng)一個(gè)ClassLoader需要加載類(lèi)的時(shí)候,這個(gè)ClassLoader會(huì)委托其父加載器去完成:首先Bootstrap ClassLoader加載器嘗試加載該類(lèi)妒茬。失敗則把工作交給ExtClassLoader担锤。ExtClassLoader失敗就把工作交給AppClassLoader蔚晨。
如果三個(gè)默認(rèn)類(lèi)加載器都加載失敗乍钻,工作只能還給發(fā)起工作的ClassLoader,由這個(gè)加載器自行選擇加載類(lèi)的文件系統(tǒng)或URL铭腕。如果所有加載器都無(wú)法加載這個(gè)類(lèi)的話银择,JVM就拋出ClassNotFoundException異常。
如果按照這個(gè)加載步驟成功累舷,類(lèi)加載器會(huì)把這個(gè)類(lèi)載入內(nèi)存浩考,初始化并返回實(shí)例。
雙親委托
使用雙親委托是為了兩個(gè)目的:運(yùn)行安全和避免重復(fù)加載被盈。
后者容易理解析孽,類(lèi)已經(jīng)被父加載器加載的話搭伤,子加載器沒(méi)有必要再次重復(fù)加載;對(duì)于前者袜瞬,有些系統(tǒng)級(jí)的類(lèi)涉及到整個(gè)JVM的運(yùn)行安全怜俐,僅能通過(guò)Bootstrap ClassLoader加載。如果不使用雙親委托邓尤,使用自定義的類(lèi)來(lái)動(dòng)態(tài)替代Java核心定義類(lèi)型拍鲤,后續(xù)系統(tǒng)將完全處于危險(xiǎn)和混亂之中。
ClassLoader層次結(jié)構(gòu)
先編寫(xiě)類(lèi)加載的代碼汞扎,其中Man
是一個(gè)自行定義的普通類(lèi)
final String dir = "file:/Users/phantomVK/repositories/intelliJ/cl/src";
URLClassLoader loader = new URLClassLoader(new URL[]{new URL(dir)});
Class clazz = loader.loadClass("com.phantomvk.Man");
ClassLoader classLoader = clazz.getClassLoader();
while (classLoader != null) {
System.out.println(classLoader);
classLoader = classLoader.getParent();
}
System.out.println(classLoader);
加載Man的類(lèi)加載器顯示結(jié)果
sun.misc.Launcher$AppClassLoader@4b67cf4d
sun.misc.Launcher$ExtClassLoader@61bbe9ba
null
加載層次:
- Man類(lèi)的類(lèi)加載器是AppClassLoader
- AppClassLoader的類(lèi)加載器是ExtClassLoader
- ExtClassLoader的類(lèi)加載器是BootstrapLoader季稳。
BootstrapLoader由C++語(yǔ)言實(shí)現(xiàn)而不是Java,不運(yùn)行在JVM管理的內(nèi)存區(qū)中澈魄。所以ExtClassLoader的類(lèi)加載器沒(méi)法顯示BootstrapLoader的引用地址景鼠,只能顯示null。
自定義ClassLoader
自定義ClassLoader比較簡(jiǎn)單
- 只需要繼承ClassLoader父類(lèi)
- 僅重寫(xiě)Class<?> findClass(String name)方法痹扇,指定然后返回這個(gè)類(lèi)
- 剩余的加載過(guò)程由父類(lèi)完成莲蜘,無(wú)需手動(dòng)處理。