1. 概述
在Java代码中,类型(类或者接口)的加载、连接与初始化过程都是在程序运行期间完成。
Java类的从加载进内存直到卸载出内存经过加载、连接(验证、准备、解析)、初始化,其中加载、验证、准备、初始化、卸载这些阶段的顺序是确定,对于解析的阶段,有可能是在类初始化之后才会解析,常见的场景是Java的动态绑定。下图是类的生命周期流程图。
类的生命周期
2. Java类结束生命周期情况
- 执行了*System.exit()*方法;
- 程序正常执行结束;
- 程序在执行的过程中发生异常或错误终止;
- 操作系统的错误导致Java虚拟机进程终止;
3. 类的加载、连接与初始化
- 加载:
查找并加载类的二进制的字节流数据; - 连接:
(a) 验证:确保被加载的类的正确性,包括文件格式验证(是否以魔数开头等)、元数据验证(是否有父类、是否与继承类字段、方法冲突等)、字节码验证(语义合法、符合逻辑验证等)、符号应用验证。
(b) 准备:为类的静态变量分配内存,并初始化默认值。这些变量所使用的内存都是在类的方法区中进行分配。
(c) 解析:把类中的符号引用转换为直接引用。 - 初始化:为类的静态变量赋予正确的初始值。
Java程序对类的使用有主动使用和被动使用,所有的Java虚拟机实现必须在类或接口被Java程序首次主动使用时才会初始化他们。
4. 初始化有且只有5种情况
(1) 创建类的实例(new)、访问某个类或接口的静态变量,或者对该静态变量赋值、调用类的静态方法;
(2) 反射(如Class.forName(“com.test.Test”));
(3) 初始化一个子类
(4) Java虚拟机启动时候标明为启动类(包含*main()*方法的类)
(5) JDK1.7 开始提供的动态语言支持:
java.lang.invoke.MethodHandle 实例的解析结果 REF_getStatic, REF_putStatic, REF_invokeStatic 句柄对应的类没有初始化,则初始化。
后面会补充针对初始化的5种情况的代码实例