目录
概述
虚拟机把描述类的数据从 Class 文件加载到内存,并对数据进行校验、转换解析和初始化, 最终形成可以被虚拟机直接使用的 Java 类型,这就是虚拟机的类加载机制。
与那些编译时需要进行连接的语言不同,Java 语言里面,类型的加载、连接和初始化都是在程序运行期间完成的,这种策略虽然会令类加载时增加一些性能开销,但是是为 Java 应 用程序提供高度的灵活性,Java 里天生可以动态扩展的语言特性就是依赖运行期动态加载和动态连接这个特点实现的。
类加载的时机
一个类型从被加载到虚拟机内存中开始,到卸载出内存为止,整个生命周期包括:加载、验证、准备、解析、初始化、使用和卸载。
加载、验证、准备、初始化和卸载这 5 个阶段的顺序是确定的,类的加载过程必须按照这种 顺序开始,而解析阶段在某些情况下可以在初始化阶段之后再开始,这是为了支持 Java 语 言的运行时绑定(动态绑定)。
什么时候开始类加载的第一阶段?
- 遇到 new、getstatic、putstatic、invokestatic 这 4 条字节码指令时,如果类没有过初 始化,则需要先初始化。最常见的场景如下:
- 使用 new 实例化对象时;
- 读取或设置一个类的静态字段时;
- 调用一个类的静态方法时;
- 反射,使用java.lang.reflect包的方法对类型进行反射调用时;
- 如果一个类的父类尚未初始化,那么先触发其父类的初始化;
- main 方法所在类;
- java.lang.invoke.MethodHandle 实例最后的解析结果 REF_getStatic、REF_putStatic、 REF_invokeStatic 的方法句柄,并且这个方法句柄所对应的类没有进行过初始化,则需要先 触发其初始化;
- 当一个接口定义了JDK 8新加入的默认方法(被default关键字修饰的接口方法)时,如果这个接口的实现类发生了初始化,那该接口要在其之前被初始化。
两个需要特别注意的地方
- 只有直接定义一个静态字段的类才会被初始化,因此通过其子类来引用父类中定义的静态字段,只会触发父类的初始化而不会触发子类的初始化。至于是否要触发子类的加载和验证, 在虚拟机规范中并未明确确定,这点取决于虚拟机的具体实现。
当一个类在初始化时,要求其父类全部都已经初始化过了,但是一个接口在初始化时,并不要求其父类接口全部都完成了初始化,只有在真正使用到父接口(如引用接口中定义的常量) 的时候才会初始化。
类的主动引用和被动引用
类的主动引用一定会触发初始化,类的被动引用不会发生类的初始化。
主动引用:
- new对象
- 引用静态变量(关键字static和final)和静态方法
- 反射
- main方法所在类
- 当前类的所有父类
被动引用:
- 访问一个类的静态变量时,只有真正声明这个静态变量的类才会被初始化
- 通过数组定义引用类,如下这种:
class A{ int age; int name; } class B{ public static void main(String[] args){ A[] aa=new A[10]; } }
- 引用常量(存在方法区的运行时常量)即:一个接口在初始化时,并不要求其父类接口全部都完成了初始化,只有在真正使用到父接口(如引用接口中定义的常量) 的时候才会初始化。