我们先来看一张图,个人对类加载过程的总结:图比较丑 见谅.
通过这个图片先大致了解一下类加载的流程,下面我们从上到下依次来解释.
1.加载:
加载是类加载过程的一个阶段, 主要完成下列三件事.
1)通过类的全限定名获取类的二进制字节流
2)将二进制字节流所代表的静态存储结构转化为方法区的运行时数据结构.
3)内存中生成代表类的Class对象,作为类的访问入口
2.验证:
验证为了确保Class文件的信息符合虚拟机要求,不会危害JVM的安全性.
1)文本格式验证.
2)元数据验证
3)字节码验证
4)符号引用验证
3.准备:
准备阶段是正式为类变量分配内存并且设置类变量初始值的阶段,这些变量都将在方法区中进行分配.
注意:
- 此时进行内存分配的变量仅包括static变量
此时进行初始化的值是指是数据变量的零值.
例如public static int value =123;
这个变量在准备阶段分配的值为0, 并不是123,因为此时并没有执行任何java方法.
如果存在ConstantValue属性,那么将按照这个属性的值进行初始化,
ConstantValue指的是被static final修饰的变量.
4.解析:
将符号引用替换为直接引用.
符号引用:通过符号来描述引用的目标
例: String A = "abc";
A就是ABC的符号引用
直接引用:
直接引用可以使指向目标的指针,偏移量或者能定位到目标的句柄.
此时A会被替换成 0x0000XXX的内存地址
5.初始化
在初始化阶段才会真正的执行类中定义的java代码,初始化阶段是执行类构造器的过程.
JVM会保证在子类调用clinit方法执行之前,父类的已经执行完毕.
父类静态语句块优先于子类.
总结:
需要注意的点是,两个初始化过程不要搞混了.
在准备阶段的初始化,仅仅为静态变量分配内存空间并且赋零值.
在初始化阶段才真正执行java代码.
所以类加载的初始化顺序:
1.为静态变量分配内存空间并且赋零值.
2.父类静态成员,方法执行
3.子类静态成员,方法执行.
4.父类代码块,成员变量,构造函数
5.子类代码块,成员变量,构造函数.
或者记住优先级:
静态>非静态
父类>子类
构造函数同级下优先级最低.
执行顺序自上而下