类加载流程
![](https://img.haomeiwen.com/i5460809/326fc7b26e984d31.png)
加载阶段
加载阶段分为 虚拟机启动加载和运行时期加载。
- 虚拟机启动加载:
加载JAVA_HOME/lib/下的rt.jar下的.class文件
使用-XX:+TraceClassLoading查看加载类信息。
- 运行时期加载:
- 通过一个类的全限定名(包名+类名)来获取该class文件的二进制字节流。
- 将字节流的静态存储结构转化为方法区的运行时数据结构。
- 为上面的class文件,在堆中生成一个java.lang.Class对象对该类的数据访问入口。
- 什么时候主动使用会加载?
- 创建类实例时,如使用new关键字、反射、克隆、反序列化
- 调用类静态方法,使用字节码invokestatic指令
- 使用类或接口静态字段时(final修饰除外),使用getstatic或者putstatic指令。
- 使用java.lang.reflect包中的方法反射类的方法。
- 初始化子类,先初始化父类。
- 启动虚拟机,含main方法的那个类。
例1:初始化子类,引发虚拟机先初始化父类
public class FatherClazz {
static {
System.out.println("FatherClazz初始化");
}
}
public class SonClazz extends FatherClazz {
static {
System.out.println("SonClazz初始化");
}
}
public class ClassLoadTest {
public static void main(String[] args) {
SonClazz sonClazz = new SonClazz();
}
}
![](https://img.haomeiwen.com/i5460809/3996f0045f640068.jpg)
例2:使用类静态字段
public class FatherClazz {
static {
System.out.println("FatherClazz初始化");
}
public static int fv = 100;
}
public class ClassLoadTest {
public static void main(String[] args) {
System.out.println(SonClazz.fv);
}
}
![](https://img.haomeiwen.com/i5460809/8af3fb21621e5fd0.jpg)
例3:数组类初始化,首次初始化数组元素时加载
public static void main(String[] args) {
FatherClazz[] arr = new FatherClazz[5];
System.out.println("start 1");
arr[0] = null;
System.out.println("start 2");
arr[0] = new FatherClazz();
System.out.println("start 3");
}
![](https://img.haomeiwen.com/i5460809/2aff61e385466f0a.jpg)
例4:final修饰的常量所在类不会被加载
public class FatherClazz {
static {
System.out.println("FatherClazz初始化");
}
public static final int fv = 100;
}
public class ClassLoadTest {
public static void main(String[] args) {
System.out.println(FatherClazz.fv);
}
}
![](https://img.haomeiwen.com/i5460809/37f70e941f85abd5.jpg)
连接阶段
- 验证过程:
这个阶段主要是验证被加载的类是否正确。目的是为了确保Class文件的字节流中包含的信息符合当前虚拟机的要求,并且不会危害虚拟机自身的安全。
流程:文件格式验证、元数据验证、字节码验证、符号引用验证 - 准备过程:
该阶段就是正式为类变量分配内存并设置类变量的初始值,注意类变量使用的内存在方法区上分配。(类变量是static修饰的变量,不包括实例变量,实例变量将会在对象实例化的时候随着对象一起分配在Java堆上)。 - 解析过程:
该阶段就是将jvm中常量池内的符合引用替换为直接引用的过程。符号引用、直接引用
初始化阶段
初始化阶段做的事就是执行类的初始化方法<clinit>,在我看来这个标识就是类构造器。方法<clinit>是由编译器自动生成的。就是将static变量赋值指定的值并且static静态代码块中赋值指定值。赋值指定值按顺序来。
public class SonClazz extends FatherClazz {
public static int sv = fv;
}
public class ClassLoadTest {
public static void main(String[] args) {
System.out.println(SonClazz.sv);
}
}
public class FatherClazz {
public static int fv = 100;
static {
fv = 200;
}
}
![](https://img.haomeiwen.com/i5460809/544c9c688a800384.jpg)
public class FatherClazz {
static {
fv = 200;
}
public static int fv = 100;
}
![](https://img.haomeiwen.com/i5460809/fe36213b41281fcd.jpg)
网友评论