# Class 初始化过程是什么
# 类加载的过程
我们可以先简单说下类加载的过程,大致分为:
** 加载、验证、准备、解析、初始化 ** 几个阶段,使用和卸载都是已经加载完成之后,属于类的生命周期的两个阶段

# 类的初始化过程
下面我们就重点分析类的初始化的过程:
类的初始化阶段,是真正开始执行类中定义的 java 程序代码 (字节码) 并按程序员的意图去初始化类变量的过程。更直接地说,初始化阶段就是执行类构造器 < clinit>() 方法的过程。 <clinit>() 方法是由编译器自动收集类中的所有类变量的赋值动作和静态代码块 static {} 中的语句合并产生的,其中编译器收集的顺序是由语句在源文件中出现的顺序所决定 ,重点就是类变量和静态代码块按源文件中定义的顺序决定执行顺序
关于类初始化的顺序 **(静态变量、静态初始化块:决于它们在类中出现的先后顺序)>(普通成员变量、初始化代码块:决于它们在类中出现的先后顺序)> 构造器 **
关于类初始化的详细过程,参见 Java 虚拟机规范一书中(有兴趣的同学可以看下英文原版的 Java 虚拟机规范:Java® Virtual Machine Specification 8),详细过程如下:
- 每个类都有一个初始化锁 LC,线程获取 LC,这个操作会导致当前线程一直等待,直到获取到 LC 锁
- 如果 C 正在被其他线程初始化,当前线程会释放 LC 锁进入阻塞状态,并等待 C 初始化完成。此时当前线程需要重试这一过程。执行初始化过程时,线程的中断状态不受影响
- 如果 C 正在被本线程初始化,即递归初始化,释放 LC 并且正常返回
- 如果 C 已经被初始化完成,释放 LC 并且正常返回
- 如果 C 处于错误状态,表明不可能再完成初始化,释放 LC 并抛出异常 NoClassDefFoundError 异常
- 否则,将 C 标记为正在被本线程初始化,释放 LC;然后,初始化那些 final 且为基础类型的类成员变量
- 如果 C 是类而不是接口,且 C 的父类 Super Class(SC)和各个接口 SI_n(按照 implements 子句中的顺序来)还没有初始化,那么就在 SC 上面递归地进行完整的初始化过程,如果有必要,需要先验证和准备 SC ;如果 SC 或 SIn 初始化过程中抛出异常,则获取 LC,将 C 标记为错误状态,并通知所有正在等待的线程,然后释放 LC,然后再抛出同样的异常。
- 从 C 的 classloader 处获取 assertion 断言机制是否被打开
- 接下来,按照文本顺序执行类变量初始化和静态代码块,或接口的字段初始化,把它们当作是一个个单独的代码块。
- 如果执行正常,那就获取 LC,标记 C 对象为已初始化,并通知所有正在等待的线程,然后释放 LC,正常退出整个过程
- 否则,如果抛出了异常 E 那么会中断退出。若 E 不是 Error,则以 E 为参数创建新的异常 ExceptionInInitializerError 作为 E。如果因为 OutOfMemoryError 导致无法创建 ExceptionInInitializerError,则将 OutOfMemoryError 作为 E。
- 获取 LC,将 C 标记为错误状态,通知所有等待的线程,释放 LC,并抛出异常 E
可以看到 JLS 确实规定了父类先初始化、static 块和类变量赋值按照文本顺序来解析