文章目录
类加载全程
- 有助于了解JVM 运行过程
- 更深入了解java动态性,解热部署,动态加载,提高程序灵活性。
JVM 类加载流程图
加载
- 将class 文件字节码内容加载到内存中,并将这些静态数据转换成方法区中运行时的数据结构,在堆中生成一个代表该类的 java.lang.Class 对象,该过程需要类加载器参与。
- 链接 将Java类二进制代码合并到JVM 运行状态中的过程。
- 验证 确保加载的类信息符合JVM 规范,没有安全方面问题。
- 准备 正式为类变量(static变量) 分配内存并且设置类变量的初始值,这些内存都在方法区进行分配。
- 解析 虚拟机常量池内的符号引用替换为直接引用过程。
- 初始化
- 初始化阶段是执行类构造器 方法的过程,类构造器()方法是由编译器自动收集类中所有类变量的赋值动作跟静态语句块中语句合并产生。
- 初始化一个类的时候 会优先初始化父类。
- JVM 会保证一个类的 方法在多线程环境中被正确加速同步。
类加载全过程
-
类的主动引用(一定发生类的初始化)
- new 一个类对象
- 调用静态方法跟静态成员。
- 调用java.lang.reflect包进行包的反射对类进行反射调用。
- 虚拟机启动,如java HelloWorld 一定先初始化HelloWorld 先启动main 方法所在类。
- 初始化类时候 父类先被初始化
-
类的被动引用(不会发生类初始化)
- 当访问一个静态域时,只有真正声明这个域的类才会被初始化, 通过子类引用父类静态变量,子类不会初始化。
- 通过数组引用不会导致类初始化。
- 引用常量不会触发类初始化 (常量在编译阶段就存入类常量池了)
package mytest;
public class Client
{
static
{
System.out.println("静态初始化Demo01"); // 第一个执行
}
public static void main(String[] args) throws Exception
{
System.out.println("Demo01的main方法!");
System.out.println(System.getProperty("java.class.path"));
//主动引用
new A(); // new 一个对象
System.out.println(A.width); // 调用类属性
Class.forName("com.bjsxt.test.A"); // 反射调用
//被动引用
System.out.println(A.MAX); //引用常量不会触发类初始化 (常量在编译阶段就存入类常量池了)
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 MAX = 100;
static
{
System.out.println("静态初始化类A");
width = 300;
}
public A()
{
System.out.println("创建A类的对象");
}
}
class A_Father extends Object
{
static
{
System.out.println("静态初始化A_Father");
}
}