1. java 类加载机制
- java 程序启动的时候,会去启动一个jvm进程, jvm会进行一些初始的操作,比如:获取系统参数等; 然后会创建一个启动类加载器, 将部分使用到的变量加载到内存中。 同时也会去创建 一个扩展类加载器和系统类加载器
2. 三个加载器之间的关系
- 服务器动时, java虚拟机第一个加载的类就是启动类加载器, bootStrapClassLoader 启动类加载器, 它由c++编写的,所以系统里面看到的时候是 null
- 扩展类加载器:extClassLoader 是由启动类加载器加载, 并且将他的 parent 置为null 同时继承 urlClassLoader
- 系统类加载器: appClassLoader 是由启动类加载器加载的, 并且将系统类加载器的parent 置为 启动类加载器, 同时继承urlClassLoader
- 在此的parent 并不是继承的关系, 而是类加载的时候的一个实例属性, 用于类加载的时候的委托对象, 具体在继承的 ClassLoader 的声明
public class Test {
public static void main(String[] args) {
ClassLoader systemLoader = Test.class.getClassLoader();
System.out.println("系统类加载器 路径==》" + systemLoader);
ClassLoader extLoader = systemLoader.getParent();
System.out.println("配置类加载器 == 》 "+ extLoader);
ClassLoader bootStrapClassLoader = extLoader.getParent();
System.out.println("启动类加载器 ==》" + bootStrapClassLoader);
}
}
3. 三个加载器的加载路径
- 加载器加载路径如下
public class Test {
public static void main(String[] args) {
System.out.println("系统类加载器默认加载地址" + System.getProperty("sun.boot.class.path"));
System.out.println("配置类加载器默认加载地址" + System.getProperty("java.ext.dirs"));
String classPath = System.getProperty("java.class.path");
System.out.println("系统类加载器加载路径:==》 " + classPath);
URLClassLoader urls = (URLClassLoader) ClassLoader.getSystemClassLoader();
//可以断点查看具体情况
urls.getURLs();
}
}
4. 双亲委派模型
双亲委托模型是指: java虚拟机在加载一个类时,默认首先采用系统类加载器去加载用到的class类,采用的是双亲委托加载机制。 当前系统类加载器加载一个类时, 先委托给其双亲进行加载, 双亲是指:当前类加载器中的parent 属性所指向的类加载器
双亲类加载器同样委托给自己的双亲去加载,如此反复, 直到没有找到双亲为止,也就是当前类的双亲为扩展类加载器,其parent为启动类加载器为止, 如果双亲没有找到对应的类, 则自己在自己的classpath中加载
类加载器在加载类时,只能向上递归委托其双亲进行类加载,而不可能从双亲再反向委派当前类加载器来进行类加载
5. 问题:
- 为什么需要委派模型
- 使java类加载有了层级关系
- 通过委派的方式,可以避免类的重复加载,当父加载器已经加载过某一个类时,子加载器就不会再重新加载这个类
- 通过双亲委派的方式,还保证了安全性。因为Bootstrap ClassLoader在加载的时候,只会加载JAVA_HOME中的jar包里面的类,如java.lang.Integer,那么这个类是不会被随意替换的,除非有人跑到你的机器上, 破坏你的JDK,可以避免有人自定义一个有破坏功能的java.lang.Integer被加载。这样可以有效的防止核心Java API被篡改。
父子加载器”之间的关系是继承吗
双亲委派模型中,类加载器之间的父子关系一般不会以继承(Inheritance)的关系来实现,而是都使用组合(Composition)关系来复用父加载器的代码的。
双亲委派是怎么实现的
实现双亲委派的代码都集中在java.lang.ClassLoader的loadClass()方法之中: 1、先检查类是否已经被加载过 2、若没有加载则调用父加载器的loadClass()方法进行加载 3、若父加载器为空则默认使用启动类加载器作为父加载器。 4、如果父类加载失败,抛出ClassNotFoundException异常后,再调用自己的findClass()方法进行加载。