类加载与双亲委派

返回 面试 · 速查全集 Java常见面试题

详解 类加载


类加载过程

加载 → 验证 → 准备 → 解析 → 初始化
      ├──── 链接 ────┤
阶段做什么
加载读 .class,生成 Class 对象
验证字节码安全、格式合法
准备静态变量赋默认值(0 / null)
解析符号引用 → 直接引用
初始化执行 <clinit>,静态块、静态字段实际赋值

static int x = 10:准备阶段 x=0,初始化阶段 x=10。


何时初始化(主动引用)

  • new 实例
  • 访问静态字段/静态方法(非编译期常量)
  • Class.forName()
  • 子类初始化前先初始化父类
  • main 的主类

不初始化:子类引用父类静态字段(只初始化父类);MyClass[] arr = new MyClass[10];编译期常量 static final


类加载器层次

Bootstrap(C++,Java 中为 null)
  └─ Extension
      └─ Application(classpath)
          └─ Custom ClassLoader

双亲委派模型

流程:收到加载请求 → 先委派给父加载器 → 父找不到 → 子才自己加载。

加载 com.example.User
  → App 委派 Extension
    → Extension 委派 Bootstrap
      → Bootstrap 找不到
    → Extension 找不到
  → App 从 classpath 加载

目的

  • 保证 核心类java.lang.*)由 Bootstrap 加载,防止伪造(如自定义 java.lang.String
  • 避免类重复加载

打破双亲委派(了解)

场景做法
SPI(JDBC、JNDI)线程上下文类加载器 TCCL
TomcatWebApp 优先加载自己的类,隔离多应用
热部署 / 插件自定义 ClassLoader 加载新 jar

常见面试题

Q:Class.forName 和 ClassLoader.loadClass 区别?
A:forName 默认会初始化类;loadClass 通常只加载不初始化(取决于实现与调用链)。

Q:同一个类可以被两个 ClassLoader 加载吗?
A:可以加载两份,在 JVM 中视为不同类,不能互相赋值(Tomcat 隔离)。

Q:准备和初始化区别?
A:准备赋默认值;初始化执行 static 块和显式赋值。


相关笔记