Java类加载机制的核心是理解.class文件如何变为内存对象,过程包括加载、验证、准备、解析、初始化五个阶段,由类加载器按双亲委派模型执行,且仅在主动引用时触发初始化。

从零开始学Java类加载机制,关键不是死记五个阶段名称,而是理解“类怎么从磁盘上的.class文件变成内存里能用的对象”。整个过程有明确触发点、清晰分工和可观察现象,掌握它不需要先啃完《深入理解JVM》,而是从写代码、看输出、改一行再运行开始。
从一个new语句开始观察
写一段最简单的代码并运行:
public class Demo {
static { System.out.println("Demo 类初始化了"); }
public static void main(String[] args) {
new Demo();
}
}
你会看到控制台只输出一次“Demo 类初始化了”。这背后就是完整的加载→验证→准备→解析→初始化流程。重点观察两点:
- static块只在第一次使用该类时执行,说明初始化不是编译时发生的
- 没有显式import或路径配置,JVM也能找到Demo.class,说明有默认查找逻辑
搞懂三个核心概念不绕弯
类加载器:不是抽象概念,是真实存在的Java对象(比如AppClassLoader),负责找.class文件。系统自带三类:
- Bootstrap:加载rt.jar里的核心类(如String),由C++实现,getClassLoader()返回null
- Extension:加载jre/lib/ext下的扩展jar
- Application:加载CLASSPATH里的用户类,也就是你写的代码默认由它加载
双亲委派:不是设计模式术语,是加载行为规则——每个加载器先问父类“你认识这个类吗?”,父类说不认识才自己动手。好处是防止自己写的java.lang.String被恶意替换。
主动引用 vs 被动引用:决定类是否触发初始化。以下会触发:
- new Demo()
- Demo.staticField = 1
- Demo.doSomething()
- Class.forName("Demo")
以下不会触发:
- Demo[] arr = new Demo[5](只创建数组类,不加载Demo)
- System.out.println(Demo.CONSTANT)(final static常量在编译期就内联了)
用工具验证比背理论更有效
加JVM参数让加载过程“开口说话”:
java -XX:+TraceClassLoading Demo → 显示所有被加载的类名
java -XX:+TraceClassResolution Demo → 显示符号引用何时转为直接引用
java -verbose:class Demo → 输出更详细的加载路径和加载器信息
配合这些参数运行上面的Demo,你会看到类似这样的输出:
[Loaded Demo from file:/path/to/classes/]
[Loaded java.lang.Object from shared objects]
说明Demo由文件系统加载,而Object来自共享库——这就是双亲委派在起作用。
动手改一点代码加深理解
把Demo改成父子类结构:
class Parent { static { System.out.println("Parent init"); } }
class Child extends Parent { static { System.out.println("Child init"); } }
public class Test { public static void main(String[] args) { new Child(); } }
运行结果一定是:
Parent init
Child init
这验证了“子类初始化前必须先初始化父类”的规则。再试试只写System.out.println(Parent.staticField),你会发现只有Parent init输出——Child根本没被加载。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











