前言:
类加载机制,指JVM将Class对象加载到内存,并对其进行解析、校验、初始化,形成最终能够被JVM直接使用的Java类型的全过程。
为什么要研究类的加载机制?
- 有助于了解JVM的运行过程;
- 能更深入了解Java的运行的动态性,提高程序灵活性。
我们通过几个要点来加深认识。
1、类加载的全过程
![47fff5ad906534a4117cb44b6c6607d5.png](https://i-blog.csdnimg.cn/blog_migrate/a85eeffa08aebbb002e71507d7fb9813.jpeg)
① 加载(作用)
将class文件的字节码加载到内存,并将这些静态数据转成方法区中运行时的数据结构,在堆中生成代表这个类的class对象,作为方法区数据的访问入口。
这个过程需要类加载器的参与。
② 链接
将Java类的二进制代码合并到JVM运行状态中的过程,主要有以下三个分支:
- 验证:确保加载的类信息安全规范;
- 准备:正式为类变量分配内存并设置类变量初始值得阶段;
- 解析:虚拟机常量池内的符号引用转为直接引用的过程。
③ 初始化
类加载的最后一个阶段,需要注意的有以下三点:
- 初始化阶段是执行类构造器<clinit>()方法的过程,该方法由编译器自动收集类中的所有类变量的赋值动作和静态语句块中的语句合并产生;
- 在初始化一个类时,如果一个类的父类没有被初始化,则优先初始化父类;
- 虚拟机会保证一个类的<clinit>()方法在多线程环境中被正确同步加锁。
2、类的加载引用
主要有主动引用和被动引用,其中主动引用会发生类的初始化。
① 主动(5种):
- 调用类构造器(new创建对象);
- 调用类的静态成员和静态方法(final修饰除外);
- 初始化一个类时,其父类尚未被初始化,该父类被优先初始化;
- main方法的启动(命令提示符中执行 "java 类名" 指令运行的类);
- 使用反射动态调用一个类。
② 被动(3种):
- 当访问一个静态域时,只有真正声明这个域的类才会被初始化;
- 通过数组定义的类引用,不会发生初始化;
- 引用常量不会发生类的初始化(常量在编译时已经存入常量池中)。
③ 代码测试静态初始化块在继承体系的执行顺序(主动+被动引用)
/**
* 测试静态初始化块在继承体系的执行顺序
* @author Jave Chan
*
*/
public class ClassLoaderDemo01 {
static {
System.out.println("静态初始化DEMO01");
}
public static void main(String[] args) throws ClassNotFoundException {
System.out.println("DEMO01的MAIN");
System.out.println(System.getProperty("java.class.path"));
//主动引用
new A(); //如果继承,则先加载父类的static块
System.out.println(A.width);
Class.forName("cn.study.seventeenthNotesAndReflectAndBytecodeAndClassupload.testClassLoader.A");
System.out.println("########################");
//被动引用
System.out.println(A.min);
A[] as = new A[10];
System.out.println(B.width); //子类调用父类的静态域时不会加载父类的静态块
}
}
class B extends A {
static {
System.out.println("静态初始化块B");
}
}
class A extends A_Father {
public static int width = 100; //静态变量(静态域) field
public static final int min =2;
static {
System.out.println("静态初始化A");
width = 300;
}
}
class A_Father {
static {
System.out.println("静态初始化A_Father");
}
}
![9bfdcfcf728fbe104ade2a6f8abe7bcd.png](https://i-blog.csdnimg.cn/blog_migrate/2389c395c631c334d8d43df3e40b85d7.png)
3、类加载器的缓存
- 标准Java类加载器可以按要求查找类,但该类一旦被加载到类加载器中将维持(缓冲)一段时间。JVM GC(垃圾回收机制可以回收这些对象)。
4、类加载器的层次结构(树状)
类加载器主要有四种,以树形结构自下往上加载,如下图:
![4e19e4fd61581df8d2775ef7001bb9d3.png](https://i-blog.csdnimg.cn/blog_migrate/d52d5b9dd63fb4d5f3ed992d1dc925f5.jpeg)
① 引导类加载器(Bootstrap)
- 由原生代码实现,并不继承于 java.class.ClassLoader ;
- 用来加载Java的核心库;加载扩展类和应用程序类加载器,并制定他们的父加载器。
② 扩展类加载器(Extensions)
- 由 sun.misc.Launcher$ExtClassLoader 实现;
- 用来加载Java的