类在首次主动使用时被加载(如new、静态方法调用、Class.forName等),由类加载器按双亲委派模型加载:AppClassLoader→ExtClassLoader→BootstrapClassLoader,确保核心类安全、类唯一且不重复加载;自定义加载器应重写findClass而非loadClass,用于热部署等场景。

面试中被问到 Java 类加载问题,重点不是背诵概念,而是讲清楚“类什么时候被加载”“谁来加载”“怎么加载”“为什么这么设计”,并能结合常见场景说明原理和影响。
类加载的时机:不是 new 的时候才加载
很多人误以为 new 一个对象时 JVM 才去加载类。实际上,类在首次“主动使用”时才会触发初始化(<clinit></clinit>),而加载(Loading)阶段更早,通常在初始化前完成。主动使用包括:
- 创建类的实例(
new) - 调用类的静态方法
- 访问类的静态字段(
final static常量除外,它在编译期就内联了) - 反射调用(如
Class.forName("X")) - 子类初始化时,父类会先初始化
- 启动类(main 方法所在类)被 JVM 启动时
被动引用(比如子类引用父类静态字段、数组定义 MyClass[] arr)不会触发类初始化,但可能触发加载(取决于具体 JVM 实现)。
双亲委派模型:不是为了炫技,是为安全和一致性
回答时别只说“AppClassLoader → ExtClassLoader → BootstrapClassLoader”,要解释为什么需要它:
立即学习“Java免费学习笔记(深入)”;
- 防止核心类(如
java.lang.String)被用户自定义类替换,避免安全风险 - 保证类的唯一性:相同全限定名的类,由同一 ClassLoader 加载才被视为同一个类;否则即使字节码一样,也会导致
ClassCastException - 避免重复加载:委派机制天然支持复用已加载的类
可以补充一句:“如果破坏双亲委派(比如重写 loadClass 不调用 super.loadClass),要非常谨慎——Tomcat 和 OSGi 就是特例,它们需要隔离 Web 应用或模块间的类。”
自定义 ClassLoader:关键在重写 findClass,不是 loadClass
面试官常追问“怎么写一个自定义类加载器”。正确做法是:
- 继承
ClassLoader - 重写
findClass(String name),在里面读取字节码(比如从文件、网络、加密流),然后调用defineClass - 不要重写
loadClass(除非要打破双亲委派),因为默认实现已包含双亲委派逻辑
举例:热部署场景下,每次重新加载类,必须用新 ClassLoader 实例(旧的不能卸载),且确保类实例和 ClassLoader 一起被回收,否则容易内存泄漏。
常见陷阱题怎么答
遇到典型题目,用原理直接回应:
-
“静态代码块和构造器谁先执行?”→ 类初始化时先执行
<clinit></clinit>(含静态变量赋值、静态代码块),按代码顺序;对象创建时再执行<init></init>(构造器) - “两个 ClassLoader 分别加载同一个类,它们相等吗?”→ 不等。JVM 判定类是否相同,看“全限定名 + 加载它的 ClassLoader”,二者缺一不可
- “为什么 Tomcat 要自己写类加载器?”→ 需要 Web 应用间类隔离、共享基础库、支持热部署,所以用打破双亲委派的 WebAppClassLoader
不复杂但容易忽略。


















