1. 什么是类加载机制
总结一句话来说,类加载机制就是:
虚拟机将Class文件加载到内存,并对数据进行校验,转化解析和初始化,形成虚拟机可以直接使用的Java类型
2. 类加载机制的过程
当我们代码需要用到对应的类的时候,就会将对应的.Class 字节码文件加载到JVM内存来,类的加载过程(即类的生命周期)会经历以下步骤:
下面逐一解释一些关键过程所做的事情:
2.1 加载 - 查找和导入class文件
- 通过一个类的全限定名获取定义此类的二进制字节流
- 将这个字节流锁代表的静态存储结构转化为方法区的运行时数据结构
- 在Java堆中生成一个代表这个类的java.lang.Class对象,作为对方法区中这些数据的访问入口
2.2 验证 - 保证被加载类的正确性
简单点来说就是:校验.Class文件中的内容,是否符合指定的规范
- 校验文件格式
- 元数据验证
- 字节码验证
- 符号引用验证
2.3 准备 - 为类的静态变量分配内存,并将其初始化为默认值
静态变量在没有赋值的情况下,是有默认值的,因为在准备阶段会为其分配默认值
例如这样直接使用,a会输出默认值0:
public class Test18 {
private static int a;
public static void main(String[] args) {
System.out.println(a);
}
}
2.4 解析 - 把类中的符号引用转为直接引用
简单点来说就是:Java虚拟机为每个类都准备了一张方法表来存放类中所有的方法。当我们需要调用这个类的方法的时候,只需要知道这个方法在表中的偏移量就可以直接调用该方法了。通过解析操作,符号引用直接转变为目标方法在类中方法表的位置,从而使得方法可以直接被调用。
符号引用就是一组符号来描述目标,可以是任何字面量。
直接引用就是直接指向目标的指针,相对偏移量或一个间接定位到目标的句柄。
解析阶段是虚拟机将常量池内的符号引用替换为直接引用的过程。 解析动作主要针对类或接口、字段、类方法、接口方法、方法类型、方法句柄和调用限定符7类符号引用进行。
2.5 初始化 - 对类的静态变量,静态代码块执行初始化操作
一般当遇到“new” 关键字的时候,就会触发类加载到初始化的全过程,把这个类准备好,然后实例化一个对象出来;
初始化一个类的时候,当发现他的父类还没有初始化的时候,那么必须先初始化他的父类。
类加载器和双亲委派机制
类加载器
Java中有以下几种类加载器:
-
启动类加载器(Bootstrap ClassLoader)
他主要是负责加载我们在机器上安装的Java目录下的核心类;负责加载$JAVA_HOME中 jre/lib/rt.jar 里所有的class或 Xbootclassoath选项指定的jar包。由C++实现,不是ClassLoader子类。 -
扩展类加载器(Extension ClassLoader)
负责加载java平台中扩展功能的一些jar包,包括$JAVA_HOME中 jre/lib/*.jar 或 -Djava.ext.dirs指定目录下的jar包。 -
应用程序类加载器(Application ClassLoader)
这类加载器就负责去加载“ClassPath”环境变量所指定的路径中的类,其实你大致就理解为去加载你写好的Java代码吧,这个类加载器就负责加载你写好的那些类到内存里。 -
自定义类加载器
除了上面那几种之外,还可以自定义类加载器,去根据你自己的需求加载你的类
双亲委派机制
就是假设你的应用程序类加载器需要加载一个类,他首先会委派给自己的父类加载器去加载,最终传导到顶层的类加载器去加载 但是如果父类加载器在自己负责加载的范围内,没找到这个类,那么就会下推加载权利给自己的子类加载器。
总结
类加载的整个过程,可以整理为以下流程:
加入有个Demo1类 ,使用了Demo2类,那么整个过程的流程如下:
public class Demo1 {
public static void main(String[] args) {
Demo1 demo1 = new Demo1();
}
}
public class Demo2 {
}