一、概述
類加載器用來把類加載到Java虛擬機中,從JDK 1.2開始,類的加載採用雙親委託機制,這種機制能更好的保證Java平臺的安全;在雙親委託機制中,除了Java虛擬機自帶的根類加載器以外,其餘的類加載都有且只有一個父加載器
當loader1加載Sample時,會委託給自己的父類加載器“系統類加載器”,“系統類加載器”會委託給“擴展類加載器”,“擴展類加載器”會委託給“根類加載器”,但是根類加載器嘗試加載之後,並不能完成加載,便返回給“擴展類加載器”,擴展類加載器也無法加載,便返回給“系統類加載器”,“系統類加載器”可以加載,便完成了加載過程
二、Java中的三種類加載器
1. Bootstrap ClassLoader(啓動類加載器)
負責加載JAVA_HOME中jre/lib/rt.jar裏所有的class,由c++實現,不是Classload子類
2. Extension ClassLoader(擴展類加載器)
負責加載Java平臺中擴展功能的一些jar包,包括AVA_HOME中jre/lib/*.jar或-Djava.ext.dirs指定目錄下的jar包
3. App ClassLoader(系統類加載器)
負責加載classpath中的jar以及class
示例:獲取並打印類加載器的類加載路徑
/**
* 查看打印不同的類加載器的加載路徑及文件
* 1. 啓動類加載器 System.getProperty("sun.boot.class.path")
* 2. 擴展類加載器 System.getProperty("java.ext.dirs")
* 3. 系統/應用類加載器 System.getProperty("java.class.path")
*/
public class ClassLoader_03 {
public static void main(String[] args) throws IOException {
Arrays.asList(System.getProperty("sun.boot.class.path")
.split(":"))
.forEach(System.out::println);
System.out.println("----------");
Arrays.asList(System.getProperty("java.ext.dirs")
.split(":"))
.forEach(System.out::println);
System.out.println("----------");
Arrays.asList(System.getProperty("java.class.path")
.split(":"))
.forEach(System.out::println);
}
}
輸出:
/Library/Java/JavaVirtualMachines/jdk1.8.0_221.jdk/Contents/Home/jre/lib/resources.jar
/Library/Java/JavaVirtualMachines/jdk1.8.0_221.jdk/Contents/Home/jre/lib/rt.jar
/Library/Java/JavaVirtualMachines/jdk1.8.0_221.jdk/Contents/Home/jre/lib/sunrsasign.jar
/Library/Java/JavaVirtualMachines/jdk1.8.0_221.jdk/Contents/Home/jre/lib/jsse.jar
/Library/Java/JavaVirtualMachines/jdk1.8.0_221.jdk/Contents/Home/jre/lib/jce.jar
/Library/Java/JavaVirtualMachines/jdk1.8.0_221.jdk/Contents/Home/jre/lib/charsets.jar
/Library/Java/JavaVirtualMachines/jdk1.8.0_221.jdk/Contents/Home/jre/lib/jfr.jar
/Library/Java/JavaVirtualMachines/jdk1.8.0_221.jdk/Contents/Home/jre/classes
----------
/Users/lizza/Library/Java/Extensions
/Library/Java/JavaVirtualMachines/jdk1.8.0_221.jdk/Contents/Home/jre/lib/ext
/Library/Java/Extensions
/Network/Library/Java/Extensions
/System/Library/Java/Extensions
/usr/lib/java
----------
/Library/Java/JavaVirtualMachines/jdk1.8.0_221.jdk/Contents/Home/jre/lib/charsets.jar
/Library/Java/JavaVirtualMachines/jdk1.8.0_221.jdk/Contents/Home/jre/lib/deploy.jar
/Library/Java/JavaVirtualMachines/jdk1.8.0_221.jdk/Contents/Home/jre/lib/ext/cldrdata.jar
/Library/Java/JavaVirtualMachines/jdk1.8.0_221.jdk/Contents/Home/jre/lib/ext/dnsns.jar
/Library/Java/JavaVirtualMachines/jdk1.8.0_221.jdk/Contents/Home/jre/lib/ext/jaccess.jar
/Library/Java/JavaVirtualMachines/jdk1.8.0_221.jdk/Contents/Home/jre/lib/ext/jfxrt.jar
/Library/Java/JavaVirtualMachines/jdk1.8.0_221.jdk/Contents/Home/jre/lib/ext/localedata.jar
/Library/Java/JavaVirtualMachines/jdk1.8.0_221.jdk/Contents/Home/jre/lib/ext/nashorn.jar
/Library/Java/JavaVirtualMachines/jdk1.8.0_221.jdk/Contents/Home/jre/lib/ext/sunec.jar
/Library/Java/JavaVirtualMachines/jdk1.8.0_221.jdk/Contents/Home/jre/lib/ext/sunjce_provider.jar
/Library/Java/JavaVirtualMachines/jdk1.8.0_221.jdk/Contents/Home/jre/lib/ext/sunpkcs11.jar
/Library/Java/JavaVirtualMachines/jdk1.8.0_221.jdk/Contents/Home/jre/lib/ext/zipfs.jar
/Library/Java/JavaVirtualMachines/jdk1.8.0_221.jdk/Contents/Home/jre/lib/javaws.jar
/Library/Java/JavaVirtualMachines/jdk1.8.0_221.jdk/Contents/Home/jre/lib/jce.jar
/Library/Java/JavaVirtualMachines/jdk1.8.0_221.jdk/Contents/Home/jre/lib/jfr.jar
/Library/Java/JavaVirtualMachines/jdk1.8.0_221.jdk/Contents/Home/jre/lib/jfxswt.jar
/Library/Java/JavaVirtualMachines/jdk1.8.0_221.jdk/Contents/Home/jre/lib/jsse.jar
/Library/Java/JavaVirtualMachines/jdk1.8.0_221.jdk/Contents/Home/jre/lib/management-agent.jar
/Library/Java/JavaVirtualMachines/jdk1.8.0_221.jdk/Contents/Home/jre/lib/plugin.jar
/Library/Java/JavaVirtualMachines/jdk1.8.0_221.jdk/Contents/Home/jre/lib/resources.jar
/Library/Java/JavaVirtualMachines/jdk1.8.0_221.jdk/Contents/Home/jre/lib/rt.jar
/Library/Java/JavaVirtualMachines/jdk1.8.0_221.jdk/Contents/Home/lib/ant-javafx.jar
/Library/Java/JavaVirtualMachines/jdk1.8.0_221.jdk/Contents/Home/lib/dt.jar
/Library/Java/JavaVirtualMachines/jdk1.8.0_221.jdk/Contents/Home/lib/javafx-mx.jar
/Library/Java/JavaVirtualMachines/jdk1.8.0_221.jdk/Contents/Home/lib/jconsole.jar
/Library/Java/JavaVirtualMachines/jdk1.8.0_221.jdk/Contents/Home/lib/packager.jar
/Library/Java/JavaVirtualMachines/jdk1.8.0_221.jdk/Contents/Home/lib/sa-jdi.jar
/Library/Java/JavaVirtualMachines/jdk1.8.0_221.jdk/Contents/Home/lib/tools.jar
/Users/lizza/Work/idea/learner/jvm/jvm_03_classloader/target/classes
/Applications/IntelliJ IDEA.app/Contents/lib/idea_rt.jar
示例:驗證啓動類加載器加載自定義的class文件
/**
* 驗證啓動類加載器加載自定義的class文件
* 1. 將ClassLoader_01.class文件拷貝到啓動類加載器的加載路徑下:/Library/Java/JavaVirtualMachines/jdk1.8.0_221.jdk/Contents/Home/jre/classes
* 2. 運行程序驗證雙親委派機制
*/
public class ClassLoader_04 {
public static void main(String[] args) throws Exception {
CustomClassLoader loader = new CustomClassLoader("loader");
Class<?> clazz = loader.loadClass("com.lizza.ClassLoader_01");
System.out.println(clazz.hashCode());
System.out.println(clazz.getClassLoader());
}
}
輸出
1627674070
null
三、定義類加載器與初始類加載器
- 如果有一個類加載器能夠成功的加載Test類,那這個類加載器稱爲定義類加載器,所有能返回Class對象引用的類加載器(包括定義類加載器)都被稱爲初始類加載器
四、不同類加載器命名空間的關係
- 不同類加載器命名空間中的類是相互不可見的,同一個類加載器命名空間中的類是相互可見的
- 由於自類加載器的命名空間包含父類加載器的命名空間,所以父類加載器加載的類在子類加載器的命名空間中是可見的;但是由子類加載器所加載的類對父類加載器是不可見的
- 如果兩個類加載器之間沒有父子關係,那麼他們加載的類相互不可見
示例:不同的類加載器命名空間中類的可見性
/**
* 不同類加載器的命名空間的類的可見性
* 1. 刪除CLASSPATH下com.lizza.User.class文件
* 2. 將CLASSPATH下com文件夾移至Desktop
*/
public class ClassLoader_07 {
public static void main(String[] args) throws Exception {
CustomClassLoader loader1 = new CustomClassLoader("loader1", "/Users/lizza/Desktop/");
CustomClassLoader loader2 = new CustomClassLoader("loader2", "/Users/lizza/Desktop/");
Class<?> clazz1 = loader1.loadClass("com.lizza.User");
Class<?> clazz2 = loader2.loadClass("com.lizza.User");
/**
* 輸出false, 原因
* 1. loader1和loader2在加載User類時都會委託給系統類加載器去加載,
* 系統類加載器無法加載, 便一直向上委託給啓動類加載器加載器, 啓動類
* 加載器無法加載, 委託給自定義類加載器
* 2. 自定義類加載器嘗試加載並且可以成功加載, 但是loader1和loader2是兩個不同的類加載器
* 故class對象不相同
*/
System.out.println(clazz1 == clazz2);
System.out.println(clazz1.getClassLoader());
System.out.println(clazz2.getClassLoader());
Object o1 = clazz1.newInstance();
Object o2 = clazz2.newInstance();
Method method = clazz1.getMethod("setUser", Object.class);
method.invoke(o1, o2);
}
}
五、雙親委託機制的優勢
- 保證Java核心類庫的安全: 雙親委託機制保證了Java的核心類庫只會由啓動類加載器加載, 不會存在 多個版本, 相互之間也是可見的
- 保證了核心類不會被自定義類所替代
- 不同的類加載器可以提供不同的命名空間, 相互之間相互隔離, 不同的類加載器加載的類相互之間不可見