完整教程:类加载_JAVA学习笔记
一、类加载是什么?
Java 代码不是直接执行的,而是先编译成字节码(.class 文件),再由 JVM 执行。
但 JVM 并不知道所有类的位置,它必须通过一种机制去:
“把类文件加载进内存,变成可以用的Class对象”
这个过程就叫——类加载(Class Loading)
1.类加载器是什么?
比喻理解:
把JVM想象成一个图书馆,类加载器就是图书管理员,负责:
- 找到需要的书(.class文件)
- 检查书是否合格(验证)
- 给书分配书架位置(准备)
- 把书的内容整理好(解析)
- 让书可以借阅(初始化)
2.类加载时机
类加载器时机 = 什么时候请管理员找书?
// 场景1:创建对象 - "我要借这本书"
Student stu = new Student(); // 管理员去找Student类
// 场景2:使用静态成员 - "我要查这本书的目录"
System.out.println(Student.school); // 管理员去找Student类
// 场景3:继承关系 - "我要借这套丛书的所有分册"
class CollegeStudent extends Student {} // 管理员会先找Student类,再找CollegeStudent类
二、类加载过程详解
三步曲:加载 → 链接 → 初始化
.class文件
│
▼
[加载] → 创建Class对象 → 放入方法区
│
▼
[链接] → 验证 → 准备 → 解析
│
▼
[初始化] → 赋值/执行静态块
1.加载(Loading)
把 .class 文件从硬件、网络、JAR包等地方加载内存中。(通俗理解是:找书和登记)
流程:
- 根据全限定类名(包名+类名)找到文件;
- 读取字节流;
- 生成一个 Class 对象放进方法区(Metaspace);
- JVM 内部会用这个 Class 对象来创建实例、调用静态方法。
// 管理员根据"书名"找到.class文件
// 创建这本书的"借阅卡"(Class对象)
Class studentClass = Student.class; // 创建借阅卡
2.链接(Linking)
| 步骤 | 含义 | 举例 |
| 验证 (Verification) | 检查字节码格式是否符合 JVM 规范、安全性检查 | 防止恶意或错误字节码 |
| 准备 (Preparation) | 为类的静态变量分配内存,设置默认值 | static String school = null; |
| 解析 (Resolution) | 把符号引用(类名、方法名)转换成直接引用(内存地址) | 把 `"com.itheima.C"` → C类对象的地址 |
详情:
2.1.验证
检查书是不是正版,有没有危险内容
// 验证字节码是否符合JVM规范
// 防止恶意代码破坏图书馆
2.2.准备
给静态变量分配空间,设默认值
class Student {
static String school = "大学"; // 准备阶段:school = null
static int count; // 准备阶段:count = 0
}
2.3.解析
把符号引用变成直接引用
class B {
C c = new C(); // 解析前:C只是个名字(符号)
// 解析后:C变成具体的内存地址
}
3.初始化
执行程序员写的初始化逻辑(赋值、静态代码块)
class Student {
static String school = "大学";
}
经过初始化后:
静态区:String school = "大学";
也就是说:到这个阶段,类才算真正可用。
三、类加载器的体系结构
JVM使用不同的加载器来加载不同来源的类。
这些类加载器存在父子关系(上下级关系),但并不是代码层面的 extends,仅仅是逻辑层面的。
比喻:把JVM想象成一个大型集团公司
JVM集团公司
│
┌─────────┼─────────┐
│ │ │
▼ ▼ ▼
董事长 部门经理 项目经理
(启动类) (平台类) (应用类)
1.启动类加载器——“董事长”
- 身份:身为创始人兼董事长,由 C++ 实现(看不到源码,null 表示)
- 职责:只信任公司最核心的员工,也就是JDK核心类库。
- 负责加载 JDK 核心类,主要加载内容:
- java.lang.*(如 String、Object)
- java.util.*(如 List、Map)
- java.io.*(如 File)
- 负责加载 JDK 核心类,主要加载内容:
特点:
- 是最底层的加载器
- 因为是C++写的,所以我们在Java代码里打印时他会显示 null。
System.out.println(String.class.getClassLoader());
// 输出 null,因为由Bootstrap加载
(意思不是“没有加载器”,而是“被董事长亲自录用了”)
2.平台类加载器——“部门经理”
- 职责:负责加载 扩展模块或中间层的工具类库。
- 主要加载内容:
- JDK自带但非核心的包(比如javax.*、一些内部工具类)。
- JDK 9 之后它取代了旧的 “扩展类加载器(ExtClassLoader)”。
- 主要加载内容:
- 特点:
- 承上启下:向董事长汇报,管理项目经理
- 管专业模块:负责特定的扩展功能
- JDK升级改名:从"扩展经理"晋升为"平台经理"
ClassLoader loader = DNSNameService.class.getClassLoader();
System.out.println(loader);
// 输出:jdk.internal.loader.ClassLoaders$PlatformClassLoader@xxxx
类比理解:
董事长不会亲自面试所有人,所以让部门经理负责审核“中层骨干”(JDK 扩展模块)。
部门经理(平台类加载器)专门管理像"网络服务"、"安全模块"这些专业部门,不上报给董事长的小事他都自己处理。
3.应用程序类加载器——“项目经理”
- 职责:负责加载 我们自己写的代码。
- 主要加载内容:
- classpath 路径下的所有类(也就是你自己写的 .class 文件)。
- 几乎所有用户代码都是它加载的。
- 主要加载内容:
- 特点:
- 一线管理:直接管理我们写的代码
- 最常用:95%的类都是他加载的
- classpath专家:负责项目路径下的所有类
public class MyClass {
public static void main(String[] args) {
// 项目经理管理我们自己的代码
System.out.println("我的类加载器: " + MyClass.class.getClassLoader());
// 输出: sun.misc.Launcher$AppClassLoader@xxx
System.out.println("String类加载器: " + String.class.getClassLoader());
// 输出: null (董事长管的)
}
}
通俗理解:
项目经理(应用程序类加载器)就是我们直接打交道的"直属领导",我们写的所有代码都归他管。
4.自定义类加载器——“外包团队”
- 职责:自己定义从哪里加载类,比如:
- 从网络加载;
- 从加密的 jar 包中加载;
- 从数据库或云端拉取 class 数据
- 关键点:
- 继承 ClassLoader;
- 通常重写 findClass() 方法;
- 默认“上级”是应用类加载器。
- 特点:
- 特殊任务:处理常规加载器做不到的事情
- 灵活机动:可以自定义加载逻辑
- 向上汇报:默认向项目经理汇报
// 比如:从网络加载类、加载加密的class文件等
public class NetworkClassLoader extends ClassLoader {
@Override
protected Class findClass(String name) throws ClassNotFoundException {
// 自定义加载逻辑:从网络下载字节码
byte[] classData = downloadClassData(name);
return defineClass(name, classData, 0, classData.length);
}
private byte[] downloadClassData(String className) {
// 模拟从网络下载
return new byte[0];
}
}
类比理解:
你公司外包了一个特殊项目,需要一个外部顾问来“按自己方式”招人。
5.完整的工作流程演示
public class ClassLoaderDemo {
public static void main(String[] args) {
System.out.println("=== 类加载器完整演示 ===");
// 1. 查看各种类的加载器
showClassLoaders();
// 2. 查看层级关系
showHierarchy();
}
static void showClassLoaders(){
System.out.println("\n1.各类的加载器:");
//董事长管理
System.out.println("String (核心类):"+String.class.getClassLoader());
System.out.println("List (工具类):"+java.util.List.class.getClassLoader());
//项目经理管理
System.out.println("当前类:"+ClassLoaderDemo.class.getClassLoader());
}
static void showHierarchy(){
System.out.println("\n2. 加载器层级关系:");
ClassLoader loader = ClassLoaderDemo.class.getClassLoader();
System.out.println("我 → " + loader); // 项目经理
System.out.println("我上级 → " + loader.getParent()); // 部门经理
System.out.println("我上上级 → " + loader.getParent().getParent()); // 董事长(null)
}
}
6.双亲委派模式
6.1.什么是双亲委派?
这个体系不是“代码继承”,而是“上下级汇报制度”。比喻: 就像公司里的请示汇报制度
工作原则(父类委派机制):
当一个下级加载器接到“加载任务”时,不会立刻干,而是先请上级试试看。
具体步骤:
当我 new String() 时:
1. 项目经理收到任务
2. 项目经理问部门经理:你能加载String吗?
3. 部门经理问董事长:你能加载String吗?
4. 董事长:我能!我来加载
5. 最终:String由董事长加载完成
public class DoubleParentDemo {
public static void main(String[] args) {
String str = new String("hello");
System.out.println(String.class.getClassLoader()); // 输出:null
}
}
6.2.双亲委派的好处:
- 避免重复加载:同一个类只加载一次
- 安全防护:防止核心API被篡改
- 结构清晰:各司其职,职责分明

浙公网安备 33010602011771号