目录
引导类加载器(启动类加载器,Bootstrap ClassLoader)
系统类加载器(应用程序类加载器,AppClassLoader)
引导类加载器(启动类加载器,Bootstrap ClassLoader)
- 使用C/C++语言实现的,嵌套在JVM内部。
- 它用来加载Java的核心库(String类……)(JAVA_HOME/jre/lib/rt.jar、resource.jar或sun.boot.class.path路径下的内容),包括加载扩展类加载器和应用程序加载器
- 没有父加载器。
- 出于安全考虑,Bootstrap启动类加载器只加载包名为java、javax、sun等开头的类
扩展类加载器(Extension ClassLoader)
- Java语言编写,由sun.misc.Launcher$ExtClassLoader实现。
- 派生于ClassLoader类
- 从java.ext.dirs系统属性所指定的目录中加载类库,或从JAVA_HOME/jre/lib/ext子目录(扩展目录)下加载类库。如果用户创建的JAR放在此目录下,也会自动由扩展类加载器加载。
系统类加载器(应用程序类加载器,AppClassLoader)
- java语言编写,由sun.misc.Launcher$AppClassLoader实现。
- 它负责加载环境变量classpath或系统属性 java.class.path指定路径下的类库
- 对于用户自定义类来说:默认使用系统类加载器进行加载
- 通过 ClassLoader.getSystemClassLoader() 方法可以获取到该类加载器
双亲委派机制
工作原理:
- 如果一个类加载器收到了类加载请求,它并不会自己先去加载,而是把这个请求委托给父类的加载器去执行;如果父类加载器还存在器父类加载器,则进一步向上委托,依次递归,最终到达顶层的引导类加载器;
- 如果父类加载器可以完成类加载任务,就成功返回,如果父类无法完成加载,子类加载器才会去加载,这就是双亲委派模式。
用一个试例来说明一下:我们假设自己建立了一个java.lang包,包下面创建了一个String,当我们运行的时候会发生什么呢?
package java.lang;
/**
* @author Mr.He
* @create 2021-05-23 21:40
*/
public class String {
static {
System.out.println("selfjava.lang.String");
}
public static void main(String[] args) {
}
}
执行结果:
错误: 在类 java.lang.String 中找不到 main 方法, 请将 main 方法定义为:
public static void main(String[] args)
否则 JavaFX 应用程序类必须扩展javafx.application.Application
说明:当加载java.lang.String时当前类加载去寻找父类加载器,一直找到 引导类加载器,引导类加载器发现可以加载java.lang.String,并且找到的路径是为JAVA_HOME/jre/lib/rt/java/lang/String,所以加载的并不是自己定义的java.lang.String,JAVA_HOME/jre/lib/rt/java/lang/String中没有main方法,所以报错
优势:
- 可以避免类的重复加载
- 防止核心的API被篡改
沙箱安全机制
这是也Java的一个安全机制,称为沙箱安全机制(在加载其他的类时,不会影响核心的类)
JVM判断两个类对象是否相同的两个条件
- 类的完整名必须一致
- 加载这个类的ClassLoader实例必须为同一个
可见性
可见性的原理是子类的加载器可以看见所有的父类加载器加载的类,而父类加载器看不到子类加载器加载的类。
单一性
单一性原理是指一个类仅被加载一次,这是由委托机制确保子类加载器不会再次加载父类加载器加载过的类。