一 什么是ClassLoader
大家都知道敲才,當我們寫好一個Java程序之后务荆,不是管是CS還是BS應用虑乖,都是由若干個.class文件組織而成的一個完整的Java應用程序,當程序在運行時闯团,即會調用該程序的一個入口函數(shù)來調用系統(tǒng)的相關功能密任,而這些功能都被封裝在不同的class文件當中,所以經(jīng)常要從這個class文件中要調用另外一個class文件中的方法偷俭,如果另外一個文件不存在的,則會引發(fā)系統(tǒng)異常缰盏。而程序在啟動的時候涌萤,并不會一次性加載程序所要用的所有class文件,而是根據(jù)程序的需要口猜,通過Java的類加載機制(ClassLoader)來動態(tài)加載某個class文件到內存當中的负溪,從而只有class文件被載入到了內存之后,才能被其它class所引用济炎。所以ClassLoader就是用來動態(tài)加載class文件到內存當中用的川抡。
其中具體加載過程為:JVM加載.class字節(jié)碼到內存,而.class文件時怎么被加載到JVM中的就是Java ClassLoader需要做的事情.
JVM什么時候加載.class文件
- 當執(zhí)行new操作時候
- 當執(zhí)行Class.forName(“包路徑 + 類名”)\ Class.forName(“包路徑 + 類名”, ClassLoader)\ ClassLoader.loadClass(“包路徑 + 類名”)
以上情況都會觸發(fā)類加載器去類加載對應的路徑去查找對應的.class文件,并創(chuàng)建Class對象.
不過第((2))方式加載字節(jié)碼到內存后生產(chǎn)的只是一個Class對象,要得到具體的對象實例還需要使用Class對象的newInstance()方法來創(chuàng)建具體實例.
再引用一段話來說明什么是類加載器:
虛擬機設計團隊把類加載階段中的“通過一個類的全限定名來獲取描述此類的二進制字節(jié)流”這個動作放到Java虛擬機外部去實現(xiàn),以便讓應用程序自己決定如何去獲取所需要的類。實現(xiàn)這個動作的代碼模塊稱為“類加載器”崖堤。
類加載器可以說是Java語言的一項創(chuàng)新侍咱,也是Java語言流行的重要原因之一,它最初是為了滿足Java Applet的需求而開發(fā)出來的密幔。雖然目前Java Applet技術基本上已經(jīng)“死掉”楔脯,但類加載器卻在類層次劃分、OSGi胯甩、熱部署昧廷、代碼加密等領域大放異彩,成為了Java技術體系中一塊重要的基石偎箫,可謂是失之桑榆木柬,收之東隅。
類加載器雖然只用于實現(xiàn)類的加載動作淹办,但它在Java程序中起到的作用卻遠遠不限于類加載階段眉枕。對于任意一個類,都需要由加載它的類加載器和這個類本身一同確立其在Java虛擬機中的唯一性娇唯,每一個類齐遵,都擁有一個獨立的類名稱空間。這句話可以表達得更通俗一些:比較兩個類是否“相等”塔插,只有在這兩個類是由同一個類加載器加載的前提下才有意義梗摇。否則,即使這兩個類來源于同一個Class文件想许,被同一個虛擬機加載伶授,只要加載它們的類加載器不同,那這兩個類就必定不相等流纹。
二 AppClassLoader
AppClassLoader應用類加載器,又稱為系統(tǒng)類加載器,負責在JVM啟動時,加載來自在命令java中的classpath或者java.class.path系統(tǒng)屬性或者CLASSPATH操作系統(tǒng)屬性所指定的JAR類包和類路徑.
public class AppClassLoaderTest {
public static void main(String[] args) {
System.out.println(ClassLoader.getSystemClassLoader());
}
}
輸出結果如下:
sun.misc.Launcher$AppClassLoader@73d16e93
以上結論說明調用ClassLoader.getSystemClassLoader()可以獲得AppClassLoader類加載器.
protected ClassLoader() {
this(checkCreateClassLoader(), getSystemClassLoader());
}
通過查看ClassLoader的源碼發(fā)現(xiàn)并且在沒有特定說明的情況下,用戶自定義的任何類加載器都將該類加載器作為自定義類加載器的父加載器.
String classPath = System.getProperty("java.class.path");
for (String path : classPath.split(";")) {
System.out.println(path);
}
通過執(zhí)行上面的代碼即可獲得classpath的加載路徑.
在上面的main函數(shù)的類的加載就是使用AppClassLoader加載器進行加載的,可以通過執(zhí)行下面的代碼得出這個結論
public class AppClassLoaderTest {
public static void main(String[] args) {
ClassLoader classLoader = Test.class.getClassLoader();
System.out.println(classLoader);
System.out.println(classLoader.getParent());
}
private static class Test {
}
}
執(zhí)行結果如下:
sun.misc.Launcher$AppClassLoader@73d16e93
sun.misc.Launcher$ExtClassLoader@15db9742
從上面的運行結果可以得知AppClassLoader的父加載器是ExtClassLoader,接下來繼續(xù)說一下ExtClassLoader類加載器.
三 ExtClassLoader
ExtClassLoader稱為擴展類加載器糜烹,主要負責加載Java的擴展類庫,默認加載JAVA_HOME/jre/lib/ext/目錄下的所有jar包或者由java.ext.dirs系統(tǒng)屬性指定的jar包.放入這個目錄下的jar包對AppClassLoader加載器都是可見的(因為ExtClassLoader是AppClassLoader的父加載器,并且Java類加載器采用了委托機制).
ExtClassLoader的類掃描路徑通過執(zhí)行下面代碼來看一下:
String extDirs = System.getProperty("java.ext.dirs");
for (String path : extDirs.split(";")) {
System.out.println(path);
}
執(zhí)行結果如下:
C:\Java\jdk1.8.0_101\jre\lib\ext
C:\Windows\Sun\Java\lib\ext
其中C:\Java\jdk1.8.0_101\jre\lib\ext路徑下內容為:
這里寫圖片描述
從上面的路徑中隨意選擇一個類,來看看他的類加載器是什么:
ClassLoader classLoader = sun.security.ec.SunEC.class.getClassLoader();
System.out.println(classLoader);
System.out.println(classLoader.getParent());
執(zhí)行結果如下:
sun.misc.Launcher$ExtClassLoader@6bc7c054
null
從上面的程序運行結果可知ExtClassLoader的父加載器為null.
四 BootstrapClassLoader
稱為啟動類加載器,是Java類加載層次中最頂層的類加載器漱凝,負責加載JDK中的核心類庫疮蹦,如:rt.jar、resources.jar茸炒、charsets.jar等愕乎,可通過如下程序獲得該類加載器從哪些地方加載了相關的jar或class文件:
URL[] urLs = sun.misc.Launcher.getBootstrapClassPath().getURLs();
for (URL url : urLs) {
System.out.println(url.toExternalForm());
}
程序執(zhí)行結果如下:
file:/C:/Java/jdk1.8.0_101/jre/lib/resources.jar
file:/C:/Java/jdk1.8.0_101/jre/lib/rt.jar
file:/C:/Java/jdk1.8.0_101/jre/lib/sunrsasign.jar
file:/C:/Java/jdk1.8.0_101/jre/lib/jsse.jar
file:/C:/Java/jdk1.8.0_101/jre/lib/jce.jar
file:/C:/Java/jdk1.8.0_101/jre/lib/charsets.jar
file:/C:/Java/jdk1.8.0_101/jre/lib/jfr.jar
file:/C:/Java/jdk1.8.0_101/jre/classes
從rt.jar中選擇String類,看一下String類的類加載器是什么
ClassLoader classLoader = String.class.getClassLoader();
System.out.println(classLoader);
執(zhí)行結果如下:
null
可知由于BootstrapClassLoader對Java不可見,所以返回了null,我們也可以通過某一個類的加載器是否為null來作為判斷該類是不是使用BootstrapClassLoader進行加載的依據(jù).另外上面提到ExtClassLoader的父加載器返回的是null,那是否說明ExtClassLoader的父加載器是BootstrapClassLoader?
Bootstrap ClassLoader是由C/C++編寫的,它本身是虛擬機的一部分壁公,所以它并不是一個JAVA類感论,也就是無法在java代碼中獲取它的引用,JVM啟動時通過Bootstrap類加載器加載rt.jar等核心jar包中的class文件紊册,之前的int.class,String.class都是由它加載比肄。然后呢,我們前面已經(jīng)分析了,JVM初始化sun.misc.Launcher并創(chuàng)建Extension ClassLoader和AppClassLoader實例芳绩。并將ExtClassLoader設置為AppClassLoader的父加載器掀亥。Bootstrap沒有父加載器,但是它卻可以作用一個ClassLoader的父加載器示括。比如ExtClassLoader铺浇。這也可以解釋之前通過ExtClassLoader的getParent方法獲取為Null的現(xiàn)象