JVM 类加载过程分为几个阶段, 分别是加载, 验证, 准备, 解析和初始化. 加载是把二进制字节码载入内存, 验证是校验字节流中包含的信息是否符合当要求, 准备是为静态变量分配内存并设置静态变量初始值, 解析是把常量池内的符号引用替换为直接引用, 初始化是执行所有静态变量的赋值动作和静态语句块中的语句. 更多详尽分析请阅读之前的文章《JVM 的类加载机制全面解析 https://mp.weixin.qq.com/s/TZhKtosd8QE1sXEnVBzVlg 》, 这里不再赘述了.
类初始化的时机
对于我们开发人员, 我认为应该具体了解一下初始化阶段什么时候在开始. JVM 规范对此做了严格规范, 有且只有以下 5 种情况必须对类进行初始化:
遇到 new,getstatic,putstatic 或 invokestatic 这四条字节码指令时, 如果类没有被初始化过, 就需要先进行初始化. 对于字节码指令不了解的同学, 可能就是一脸蒙圈了. 我们来说人话, 就是: 使用 new 关键字实例化对象的时候, 读取和设置一个类的静态字段 (不被 final 修饰的) 和调用一个类的静态方法的时候. 这样说更容易被理解一些.
使用 java.lang.reflect 包中的方法对类进行反射调用的时候, 如果类没有被初始化过, 就需要先进行初始化.
当初始化一个类的时候, 如果发现它的父类还没有被初始化过, 就需要先初始化它的父类.
JVM 会先初始化要执行的主类, 也是包含 main()方法的那个类.
当使用 JDK 1.7 的动态语言支持时, 如果 java.lang.invoke.MethodHandle 实例最后的解析结果是 REF_getStatic(使用 MethodHandle 读取类的静态字段),REF_putStatic(使用 MethodHandle 设置类的静态字段),REF_invokeStatic(使用 MethodHandle 调用类的静态方法)的方法句柄时, 如果这个方法句柄没有被初始化过, 就需要先进行初始化.
- public class SuperClass {
- static {
- System.out.println("父类正在初始化");
- }
- public static String name = "万猫学社";
- }
- public class SubClass extends SuperClass {
- static {
- System.out.println("子类正在初始化");
- }
- }
- public class OneMoreStudy {
- public static void main(String[] args) {
- System.out.println(SubClass.name);
- }
- }
- public class OneMoreStudy {
- public static void main(String[] args) {
- SuperClass[] arrays = new SuperClass[10];
- System.out.println("数组元素个数:" + arrays.length);
- }
- }
- public class ConstClass {
- static {
- System.out.println("有常量的类正在初始化");
- }
- public static final String NAME = "万猫学社";
- }
- public class OneMoreStudy {
- public static void main(String[] args) {
- System.out.println(ConstClass.NAME);
- }
- }
来源: https://www.cnblogs.com/heihaozi/p/12015094.html