1.類加載器分類
JVM支持兩種類型的類加載器,分別爲引導類加載器(Bootstrap ClassLoader)和自定義類加載器(User-Defined ClassLoader)。
這裏的自定義加載器指的不是開發人員自己定義的類加載器,而是指的所有繼承自ClassLoader的類加載器。包括擴展類加載器、應用程序類加載器、用戶自定義類加載器(程序員自己寫的)三種。
常見的類加載器如下圖所示:
第一個藍色框表示的是引導類加載器;剩下的所有的類加載器都是自定義的類加載器。BootStrapClassLoader使用C語言實現,自定義類加載器使用Java語言實現。
注意:圖中不是表示的繼承關係。擴展類加載器和系統類加載器(應用程序類加載器AppClassLoader)都是繼承自ClassLoader,所以將他們劃分爲自定義加載器。
下面的Java例子,可以幫助理解類加載器之間的關係:
public class ClassLoaderTest {
public static void main(String[] args) {
//獲取系統類加載器,輸出AppClassLoader,說明系統類加載器就是應用程序類加載器
ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
System.out.println(systemClassLoader);//sun.misc.Launcher$AppClassLoader@18b4aac2
//獲取系統類加載器的上層:輸出ExtClassLoader,說明系統類加載器的上層是擴展類加載器
ClassLoader extClassLoader = systemClassLoader.getParent();
System.out.println(extClassLoader);//sun.misc.Launcher$ExtClassLoader@1540e19d
//獲取擴展類加載器的上層:輸出null,獲取不到引導類加載器。雖然獲取不到,但是擴展類加載器的上層是引導類加載器
ClassLoader bootstrapClassLoader = extClassLoader.getParent();
System.out.println(bootstrapClassLoader);//null
//輸出AppClassLoader。說明對於用戶自定義類來說:默認使用系統類加載器進行加載
ClassLoader classLoader = ClassLoaderTest.class.getClassLoader();
System.out.println(classLoader);//sun.misc.Launcher$AppClassLoader@18b4aac2
//打印的也是null,和獲取擴展類的上層的輸出是一樣。可以證明String類使用引導類加載器進行加載的。擴展開來:---> Java的核心類庫都是使用引導類加載器進行加載的。
ClassLoader classLoader1 = String.class.getClassLoader();
System.out.println(classLoader1);//null
}
}
1.1 引導類加載器(Bootstrap ClassLoader)
1.引導類加載器使用C/C++語言實現,在JVM內部
2.用於加載Java核心類庫
3.不繼承ClassLoader
4.還用於加載擴展類加載器和應用程序類加載器
5.只加載包名爲java,javax,sun開頭的類
1.2 擴展類加載器(Extension ClassLoader)
1.使用java語言編寫,JVM自帶
2.繼承自ClassLoader
3.父類加載器爲啓動類加載器
4.從java.ext.dirs指定的路徑下加載類庫;或者從JDK安裝目錄的jre/lib/ext目錄下加載類庫。
5.如果用戶自定義的jar包放在jre/lib/ext下,也會自動由擴展類加載器加載
1.3 應用程序類加載器(AppClassLoader或者稱爲系統類加載器)
1.使用jaca語言編寫,JVM自帶
2.繼承自ClassLoader
3.父類加載器爲啓動類加載器
4.負責加載環境變量classpath或系統屬性java.class.path指定的類庫
5.java中自己寫的類都是由應用程序類加載器加載的
6.可以通過ClassLoader.getSystemClassLoader()方法獲取該類加載器
理解BootstrapClassLoader、ExtClassLoader、AppClassLoader的例子:
public class ClassLoaderTest1 {
public static void main(String[] args) {
System.out.println("**********啓動類加載器**************");
//獲取BootstrapClassLoader能夠加載的api的路徑
URL[] urLs = sun.misc.Launcher.getBootstrapClassPath().getURLs();//獲取到的是通過引導類加載的類庫的路徑(輸出參考“引導類能夠加載的類庫路徑”)
for (URL element : urLs) {
System.out.println(element.toExternalForm());
}
//從上面的路徑中隨意選擇一個類,來看看他的類加載器是什麼:引導類加載器
ClassLoader classLoader = Provider.class.getClassLoader();
System.out.println(classLoader); //輸出爲null。說明是引導類加載器加載的
System.out.println("***********擴展類加載器*************");
String extDirs = System.getProperty("java.ext.dirs");
for (String path : extDirs.split(";")) {// 輸出參考“擴展類能夠加載的類庫路徑”圖
System.out.println(path);
}
//從上面的路徑中隨意選擇一個類,來看看他的類加載器是什麼:擴展類加載器
ClassLoader classLoader1 = CurveDB.class.getClassLoader();
System.out.println(classLoader1);//sun.misc.Launcher$ExtClassLoader@1540e19d
}
}
引導類能夠加載的類庫路徑:如下圖。主要就是jre/lib/ext目錄下面的jar包。
擴展類能夠加載的類庫路徑:如下圖。主要是jre/lib/ext目錄以及java.ext.dirs指定的路徑下的jar包
1.4 用戶自定義類加載器(程序員自己寫的)
除了上面3種JVM提供的類加載器之外,程序員還可以自己定義類加載器。(簡單瞭解)
自定義加載器實現步驟:
自定義一個類加載器簡單的例子:
public class CustomClassLoader extends ClassLoader { //繼承ClassLoader
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException { //重載findClass方法
try {
byte[] result = getClassFromCustomPath(name);
if(result == null){
throw new FileNotFoundException();
}else{
return defineClass(name,result,0,result.length);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
}
throw new ClassNotFoundException(name);
}
private byte[] getClassFromCustomPath(String name){
//從自定義路徑中加載指定類:細節略
//如果指定路徑的字節碼文件進行了加密,則需要在此方法中進行解密操作。(這裏可以進行解密操作,防止class文件被反編譯)
return null;
}
public static void main(String[] args) {
CustomClassLoader customClassLoader = new CustomClassLoader();
try {
Class<?> clazz = Class.forName("One",true,customClassLoader);
Object obj = clazz.newInstance();
System.out.println(obj.getClass().getClassLoader());
} catch (Exception e) {
e.printStackTrace();
}
}
}