1. 加载
- java源代码编译为字节码;
- 然后通过类加载器 加载 字节码到
方法区
(元空间)中, - 并根据Class 文件描述在
堆
中创建 java.lang.Class 类实例对象;
注意:双亲委派机制 loadClass()会执行加载阶段
2. 链接
2.1. 验证:
验证字节码是否符合虚拟机规范 ,不合法的将会报错ClassFormatError
2.2. 准备:
为 static
静态变量、基本数据类型分配空间,设置初始值;
JDK1.6中静态变量在方法区,现在的JDK1.8 中静态变量在堆中
static静态变量分配空间和赋值是两个步骤,分配空间在准备阶段完成,赋值在初始化阶段类的构造中完成
· 如果是static final 基本类型特殊,值在编译阶段就确定了,赋值在准备中完成
· 如果是static final
引用类型,那么赋值会在初始化阶段完成;
所以类加载会导致静态变量的初始化,对应链接阶段的【准备】及之前;
new() 对象才会执行初始化阶段!才会初始化实例普通实例变量;
2.3. 解析:
指的是将【字节码】中的静态(类)常量池里的信息放入运行时常量池,符号引用会转变为直接引用;
符号引用仅仅是符号,并不知道类、方法在内存的什么位置,但是经过了解析之后,就知道了类、方法在内存的确切位置;
loadClasss()方法只加载,但不会导致类的解析和初始化
new() 会做解析和初始化
3. 初始化
初始化就是去执行类的构造函数,虚拟机会保证这个类的构造方法的线程安全;
发生时机:
初始化是懒惰
的
main方法时首先会被初始化
首次访问这个类的静态变量、静态方法(不是final的)时会初始化
子类的初始化:如果父类还没初始化会先初始化父类
子类访问父类的静态变量,只会触发父类的初始化,子类并不会被初始化
Class.forName会导致类的初始化 (newInstacnce()方法会执行无参构造)
new()
对象会导致解析和初始化;
不会导致初始化:
访问 static
final静态常量(基本类型、字符串)不会触发初始化,其都在链接的准备阶段就完成了变量的赋值;
创建类的数组不会导致初始化
使用ClassLoader默认的 loadClasss()
方法只加载,但不会导致类的解析和初始化 !