Class对象的创建时间
Class 对象的创建发生在类加载过程的「链接阶段的结束」到「初始化阶段的开始」之间 (主流 HotSpot 虚拟机实现),且一定是在类初始化之前就已创建完成,而非 "加载完后"(加载阶段仅完成二进制字节码加载,尚未形成完整的 Class 对象)。
HotSpot 虚拟机中,Class对象(也叫 "类元信息对象")的创建是加载阶段启动,但链接阶段完成:
- 加载阶段 :虚拟机读取字节码后,先在堆内存(注意:方法区存类的运行时数据,Class 对象存堆)创建一个空的 Class 对象雏形,仅记录类的全限定名、父类等基础信息,此时 Class 对象不可用;
- 链接阶段(验证 + 准备 + 解析) :虚拟机逐步完善 Class 对象的内容(如静态变量的内存地址、方法的直接引用、接口信息等);
- 链接阶段结束后、初始化阶段开始前 :Class 对象完全创建完成,此时可以通过
Class.forName()、类名.class等方式获取到这个对象,但类的静态变量还未执行用户指定的赋值(初始化阶段才做)。
方法区存类的运行时数据,Class 对象存堆
java
public class ClassObjectTiming {
// 静态变量(初始化阶段执行赋值)
public static final String TEST = "初始化完成";
static {
System.out.println("执行静态代码块(初始化阶段)");
}
public static void main(String[] args) throws ClassNotFoundException {
// 方式1:通过类名.class获取Class对象(仅触发加载+链接,不触发初始化)
Class<ClassObjectTiming> clazz1 = ClassObjectTiming.class;
System.out.println("获取Class对象:" + clazz1.getName());
System.out.println("此时静态变量值:" + clazz1.getField("TEST").get(null)); // 能获取到默认值/常量值,但未执行静态代码块
// 方式2:Class.forName(类名, false, 类加载器) ------ false表示不触发初始化
Class<?> clazz2 = Class.forName("ClassObjectTiming", false, ClassObjectTiming.class.getClassLoader());
System.out.println("再次获取Class对象:" + clazz2.getName());
// 方式3:Class.forName(类名) ------ 触发初始化
Class<?> clazz3 = Class.forName("ClassObjectTiming");
System.out.println("初始化后获取:" + clazz3.getName());
}
}
output:
获取Class对象:ClassObjectTiming
此时静态变量值:初始化完成
再次获取Class对象:ClassObjectTiming
执行静态代码块(初始化阶段)
初始化后获取:ClassObjectTiming
常见误区澄清
-
误区 1:"加载完就创建 Class 对象"
→ 加载阶段仅生成 Class 对象雏形,链接阶段(验证 / 准备 / 解析)才完成 Class 对象的初始化,加载阶段结束后 Class 对象尚未就绪;
-
误区 2:"Class 对象在方法区"
→ HotSpot 中,类的运行时数据(如常量池、方法信息)存在方法区(元空间),但
Class对象本身存放在堆内存中; -
误区 3:"获取 Class 对象就等于类初始化"
→ 只有触发《Java 虚拟机规范》中规定的 5 种 "主动使用" 场景(如 new 实例、调用静态方法、反射且不指定不初始化),才会触发初始化,单纯获取 Class 对象(如
类名.class)仅触发加载 + 链接。