类加载过程的三个阶段
-
加载
-
将类的字节码载入方法区,并创建类.class 对象
-
如果此类的父类没有加载,先加载父类
-
加载是懒惰执行
-
-
链接
-
验证 – 验证类是否符合 Class 规范,合法性、安全性检查
-
准备 – 为 static 变量分配空间,设置默认值
-
解析 – 将常量池的符号引用解析为直接引用
-
-
初始化
-
静态代码块、static 修饰的变量赋值、static final 修饰的引用类型变量赋值,会被合并成一个
<cinit>
方法,在初始化时被调用 -
static final 修饰的基本类型变量赋值,在链接阶段就已完成
-
初始化是懒惰执行
-
jdk 8 的类加载器
名称 | 加载哪的类 | 说明 |
---|---|---|
Bootstrap ClassLoader | JAVA_HOME/jre/lib | 无法直接访问 |
Extension ClassLoader | JAVA_HOME/jre/lib/ext | 上级为 Bootstrap,显示为 null |
Application ClassLoader | classpath | 上级为 Extension |
自定义类加载器 | 自定义 | 上级为 Application |
双亲委派机制
所谓的双亲委派,就是指优先委派上级类加载器进行加载,如果上级类加载器
-
能找到这个类,由上级加载,加载后该类也对下级加载器可见
-
找不到这个类,则下级类加载器才有资格执行加载
双亲委派的目的有两点
-
让上级类加载器中的类对下级共享(反之不行),即能让你的类能依赖到 jdk 提供的核心类
-
让类的加载有优先次序,保证核心类优先加载
对双亲委派的误解
下面面试题的回答是错误的
错在哪了?
-
自己编写类加载器就能加载一个假冒的 java.lang.System 吗? 答案是不行。
-
假设你自己的类加载器用双亲委派,那么优先由启动类加载器加载真正的 java.lang.System,自然不会加载假冒的
-
假设你自己的类加载器不用双亲委派,那么你的类加载器加载假冒的 java.lang.System 时,它需要先加载父类 java.lang.Object,而你没有用委派,找不到 java.lang.Object 所以加载会失败
-
以上也仅仅是假设。事实上操作你就会发现,自定义类加载器加载以 java. 打头的类时,会抛安全异常,在 jdk9 以上版本这些特殊包名都与模块进行了绑定,更连编译都过不了