public class Test {
public static void main(String[] args) {
System.out.println(Test.class.getClassLoader());
System.out.println(Test.class.getClassLoader().getParent());
System.out.println(Test.class.getClassLoader().getParent().getParent());
}
}
打印结果:
sun.misc.Launcher$AppClassLoader@18b4aac2
sun.misc.Launcher$ExtClassLoader@1a6c5a9e
null
第一行打印的是 AppClassLoader 应用类加载器,它的父类加载器是 ExtClassLoader(Ext 是 Extension的缩写)扩展类加载器,但 Ext 加载器的父类打印出来却是个 null,这是因为 启动类加载器(null)并不是 java 实现的。
一、类加载器
-
启动类加载器(BootstrapClassLoader):加载 java 的核心类库
-
扩展类加载器(ExtClassLoader ):加载 java 扩展类库
-
应用类加载器(AppClassLoader):加载 环境变量 classpath 下面的类
-
自定义类加载器(暂且忽略)
二、什么是双亲委派?
双亲委派机制是指当一个类加载器收到一个类加载请求时,该类加载器首先会把请求委派给父类加载器,当父类加载器在自己的搜索范围内找不到指定类时,子类加载器才会尝试自己去加载,如果子类加载器范围内也找不到该类,则会报 ClassNotFoundException。
通俗来讲,就是当要加载一个类时,
- 首先 AppClassLoader 会收到加载请求,但它不会直接加载这个类,而是把这个加载请求抛给它的父类加载器:ExtClassLoader ;
- 而 ExtClassLoader 收到类加载请求时,也不会直接加载这个类,而是又把这个加载请求抛给它的父类加载器:BootstrapClassLoader;
- BootstrapClassLoader 收到类加载请求后,则会在它的范围内寻找这个类,如果找到了,就直接加载,如果找不到,就把加载请求又朝它的子类加载器:ExtClassLoader 丢过去;
- ExtClassLoader 收到父类丢过来的类加载请求,就会在自己管辖范围内寻找这个类,德行跟它的父类一样,找到就加载,找不到就 抛出个异常 给子类加载器:AppClassLoader;
- AppClassLoader 收到父类丢过来的类加载请求后... 发现祖父和父亲都找不到这个类,就会自己尝试去加载,德行也是遗传的,找到就加载,找不到也会抛异常:ClassNotFoundException ,这个异常的意思是:在系统整个 java 的运行环境中,都找不到这个类。
在这里 ExtClassLoader 找不到会给 AppClassLoader 抛异常,而 BootstrapClassLoader 找不到并不会给 ExtClassLoader 抛异常,也是因为 BootstrapClassLoader 并不是 java 实现的。
--- BootstrapClassLoader 为什么不是 java 实现的?
因为启动类加载器非常重要,它要把整个 java 的环境加载到内存当中,环境还未启动,那 java 的其他任何东西就更不可能存在,所以它一定是操作系统的语言(比如 C 语言)来实现,所以那些最基础的类也都是由 启动类加载器 来加载的,所以BootstrapClassLoader 在 java 中打印出来的就是 null。
三、举例演示
1.在 D:\Test 目录下创建一个文件夹 Test.java,内容如下:
public class Test {
public static void main(String[] args) {
System.out.println("====== GrandFather ======");
}
}
2.在 D:\Test 文件夹下启动命令窗口:
3.命令行输入:javac *.java 回车,此目录下会多出一个 .class 文件
4.把 .class 文件剪切到:C:\Program Files\Java\jre1.8.0_162\classes 目录下(看自己的 jre 在哪里,classes 是自己新建的)
5.将 Test.java 内容修改,并按照 2、3步骤编译出 .class 文件
public class Test {
public static void main(String[] args) {
System.out.println("====== Father ======");
}
}
6.这次将.class 文件放到 C:\Program Files\Java\jre1.8.0_162\lib\ext\classes 下(classes 文件也是自己新建的)
7.回过头来,再将 Test.java 文件内容修改并编译
public class Test {
public static void main(String[] args) {
System.out.println("====== MySelf ======");
}
}
8.此时在命令窗口执行这个 Test.java 文件
9.将 C:\Program Files\Java\jre1.8.0_162\classes 目录下的 .class 文件删除后再执行 Test.java
10.将 C:\Program Files\Java\jre1.8.0_162\lib\ext\classes 下的 .class 文件删除后再执行 Test.java
11.删除 D:\Test 目录下的.class 文件再执行
四、双亲委派机制的作用
- 防止重复加载同一个
.class
保证数据安全; - 保证核心
.class
不能被篡改,保证Class
执行安全。