深入解析 Java 类加载机制及双亲委派模型
🔍 Java的类加载机制是确保应用程序正确运行的基础,特别是双亲委派模型,它通过父类加载器逐层加载类,避免冲突和重复加载。但在某些特殊场景下,破坏双亲委派模型会带来意想不到的效果。本文将深入解析Java类加载机制、双亲委派模型的运作原理,以及如何在特定场景下破坏这一模型。
📌 类加载机制(Class Loading Mechanism)
Java 之所以能实现【编译一次,到处运行】,很大程度上得益于类加载机制(Class Loading Mechanism) 。Java 类的加载过程主要包含以下三个主要阶段:
1.加载(Loading)
📂 通过从 .class 文件中读取字节码,并创建对应的 Class 对象。
2.链接(Linking)
- 🔍 验证(Verification) :确保字节码格式正确,不做坏事(如非法访问内存)。
- 📌 准备(Preparation) :为类的静态变量分配内存,初始化默认值。
- 🔗 解析(Resolution) :将符号引用替换为直接引用(如
String->java.lang.String)。
3.初始化(Initialization)
⚡ 执行类的 <clinit> 方法,赋值静态变量,执行静态代码块。

这些步骤构成 JVM 的类加载流程,而其中最重要的规则之一就是双亲委派模型。
🔰 双亲委派模型(Parent Delegation Model)
❓什么是双亲委派(Parent Delegation Model)?
它是一种递归委托机制,目的是保证 Java 核心类的安全性和唯一性。需要遵守以下规则:
✅ 当一个类加载器收到加载请求时,不会自己先加载,而是优先交给它的父类加载器。
✅ 只有当 所有的父类加载器都无法加载该类 时,才会由当前加载器自己尝试加载。
🛠️类加载器(ClassLoader)
🔎 常见的类加载器
| 📌 类加载器 | 作用 |
|---|---|
| Bootstrap ClassLoader (引导类加载器) | 负责加载 JDK 核心类库(如 rt.jar, java.base),由 C++ 实现,不继承 ClassLoader。 |
| Extension ClassLoader (扩展类加载器) | 负责加载 JAVA_HOME/lib/ext/ 目录下的扩展类库(如 javax 包)。 |
| Application ClassLoader (应用类加载器) | 负责加载CLASSPATH 下的类,也是 ClassLoader 的子类。 |
| Custom ClassLoader (自定义类加载器) | 通过继承 ClassLoader 来实现动态加载、加密解密、热更新等功能。 |
📜 类加载器的层级
Java 默认的类加载器层级如下:
🟠 BootstrapClassLoader (引导类加载器,加载 Java 核心类,如 `java.lang.*`)↓
🟡 ExtClassLoader (扩展类加载器,加载 `lib/ext` 目录下的类)↓
🔵 AppClassLoader (应用类加载器,加载 `classpath` 下的类)↓
🟣 Custom ClassLoader (自定义类加载器)
📦 类加载器的双亲委派模型
双亲委派机制(Parent Delegation Model) 主要用于保证类的安全性和避免重复加载。其工作流程如下:
✅ 当一个类加载器接到加载请求,它会先委派给父类加载器。
✅ 如果父类加载器能够加载这个类,就直接返回已加载的类。
✅ 如果父类加载器无法加载,才会由当前类加载器尝试加载这个类。
这个机制可以 防止核心 API(如 java.lang.String )被篡改,并且 提高类加载的效率(同一个类不会被重复加载)。

Bootstrap ClassLoader处于 最顶层,加载 JDK 自带的核心类库。Extension ClassLoader由Bootstrap ClassLoader加载,负责 JDK 的扩展类库。Application ClassLoader负责加载 用户代码(即 CLASSPATH 下的类) 。Custom ClassLoader继承ClassLoader,通常用于 热加载、自定义加密加载等。
💡举个栗子:
当你在代码中使用 String.class 时,JVM 不会 从 classpath 里去找,而是直接交给 BootstrapClassLoader 加载。这样可以确保 java.lang.String 不会被篡改。
💡 双亲委派的好处
✅ 防止核心类被篡改:确保 java.lang.Object、java.lang.String 等类的唯一性,避免被应用程序随意修改。
✅ 提高加载效率:如果某个类已经被父类加载器加载,子类加载器就不需要再重复加载。
🧠 但是,在某些特殊场景下,我们可能需要打破双亲委派机制。
🚨 破坏双亲委派机制的场景分析
尽管双亲委派模型是 Java 类加载的核心机制,但在某些特殊场景下,它需要被“破坏”或绕过。
🧠 为什么需要"破坏"双亲委派?
✅ 灵活性需求:某些场景需要动态加载用户提供的实现
✅ 模块化隔离:不同模块可能需要相同类的不同版本
✅ 热更新:运行时替换类定义
✅ SPI扩展:基础框架需要加载未知的实现类
📌 破坏双亲委派机制的主要场景
(1)JDBC SPI机制
⚠️ 现象分析
📌 DriverManager 由 BootstrapClassLoader 加载(因为 DriverManager 在 rt.jar 中)。
📌 但是数据库驱动(如 mysql-connector-java)却是由 AppClassLoader 加载的。
✅ 解决方案
📌 JDBC 采用 线程上下文类加载器(Thread Context ClassLoader, TCCL) 来动态加载驱动。
// JDBC 获取连接时的类加载方式
Connection conn = DriverManager.getConnection(url);
// 内部使用 Thread.currentThread().getContextClassLoader() 来加载驱动
(2)Tomcat 等 Web 容器
⚠️ 现象分析
📌 需要隔离不同Web应用(防止类冲突)。
📌 共享某些公共库(如Servlet API)。
✅ 解决方案
📌 每个Web应用有自己的WebappClassLoader。
📌 优先加载自己WEB-INF/classes和WEB-INF/lib下的类。
📌 共享类则委派给Common ClassLoader。
(3)JNDI服务
⚠️ 现象分析
📌 JNDI核心类由 Bootstrap 加载。
📌 但具体实现(如LDAP、RMI等)需要由应用类加载器加载。
✅ 解决方案
📌 采用 线程上下文类加载器 来动态加载 JNDI 具体实现。
(4)热部署/热替换场景
⚠️ 现象分析
📌 需要重新加载修改后的类而不重启JVM。
📌 标准的双亲委派无法实现类卸载和重新加载。
✅ 解决方案
📌 自定义类加载器实现(如JRebel)。
📌 每个类版本由不同的类加载器实例加载。
📌 梳理破坏双亲委派的几种操作
| 🎯 场景 | 🔥 破坏原因 | 💡 解决方案 |
|---|---|---|
| JDBC SPI | DriverManager 需要 AppClassLoader 加载驱动 | 线程上下文类加载器 |
| Tomcat/Web 容器 | 需要隔离不同 Web 应用 | 每个 WebApp 有自己的类加载器 |
| JNDI | 核心类由 BootstrapClassLoader 加载,具体实现需 AppClassLoader | 线程上下文类加载器 |
| 热部署 | 需要重新加载类 | 自定义类加载器(如 JRebel) |
(1) 重写 ClassLoader 的 loadClass 方法(暴力反叛)
默认的 ClassLoader 使用 loadClass() 方法实现双亲委派,如果我们不按套路来,自己定义一个 ClassLoader,并直接从文件或网络加载 class 文件,就可以绕开双亲委派规则:
public class MyClassLoader extends ClassLoader {@Overridepublic Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {// 破坏双亲委派,不委托父加载器,直接尝试自己加载if (name.startsWith("com.mycompany")) { return findClass(name);}return super.loadClass(name, resolve);}@Overridepublic Class<?> findClass(String name) throws ClassNotFoundException {byte[] bytes = loadClassData(name);return defineClass(name, bytes, 0, bytes.length);}private byte[] loadClassData(String name) {// 从文件或网络加载 class 字节码return new byte[0]; // 这里只是示例,实际要实现字节码加载}
}
这种方式通常用于 动态加载类(如热替换、插件系统) ,但可能会带来类冲突问题。
(2)线程上下文类加载器(Thread Context ClassLoader)
Java 允许在线程级别动态更换类加载器,JDBC、SPI(Service Provider Interface)机制 就是靠这个来破坏双亲委派的!
Thread.currentThread().setContextClassLoader(new MyClassLoader());
JVM 在某些地方会调用 Thread.currentThread().getContextClassLoader() 来加载类,比如 ServiceLoader 机制,这使得它可以绕过双亲委派,加载应用级别的 SPI 扩展。
(3) defineClass() 方法直接加载字节码
Java 的 defineClass() 方法可以 绕过标准的类加载流程,直接把一个字节码转换成 Class 对象,而不经过双亲委派。
byte[] classBytes = ...; // 通过 IO 读取 class 文件
Class<?> clazz = defineClass("com.example.MyClass", classBytes, 0, classBytes.length);
public class MyClassLoader extends ClassLoader {public Class<?> loadClassFromFile(String className, String path) throws IOException {byte[] classData = Files.readAllBytes(Paths.get(path));return defineClass(className, classData, 0, classData.length);}public static void main(String[] args) throws Exception {MyClassLoader loader = new MyClassLoader();Class<?> clazz = loader.loadClassFromFile("com.example.MyClass", "/path/to/MyClass.class");Object obj = clazz.getDeclaredConstructor().newInstance();System.out.println("Loaded class: " + obj.getClass().getName());}
}
📌 将一个二进制的 .class 文件数据(字节数组 b)转换成 Class<?> 对象。
📌 这个方法通常用于自定义类加载器中,以加载不是由标准类加载器(如 BootstrapClassLoader、AppClassLoader)加载的类。
📌 defineClass 仅负责定义类,不会自动执行类的初始化(不会调用 静态代码块)。
defineClass 与 loadClass 的区别
| 方法 | 作用 |
|---|---|
loadClass(String name) | 委托双亲委派机制加载类,通常不会自行加载字节码。 |
defineClass(String name, byte[] b, int off, int len) | 直接用字节数组定义类,不经过双亲委派。 |
如果你要完全绕过双亲委派机制,可以自己实现 findClass 并调用 defineClass,但通常不推荐这样做,除非是像插件系统、热加载等特殊场景。
📊 总结
⚙️ 理解Java类加载机制和双亲委派模型,是开发高效、稳定应用的基础。虽然双亲委派模型能确保类加载的一致性,但在特定需求下,灵活调整或破坏它能够带来意想不到的优化。掌握这些关键细节,将帮助你在开发过程中游刃有余。💡

相关文章:
深入解析 Java 类加载机制及双亲委派模型
🔍 Java的类加载机制是确保应用程序正确运行的基础,特别是双亲委派模型,它通过父类加载器逐层加载类,避免冲突和重复加载。但在某些特殊场景下,破坏双亲委派模型会带来意想不到的效果。本文将深入解析Java类加载机制、…...
糖尿病大模型预测及临床应用研究智能管理系统技术文档
目录 1. 数据工程规范1.1 多源数据集成1.2 特征工程架构 2. 核心模型架构2.1 分层预测网络2.2 动态血糖预测模块 3. 实时决策系统3.1 术中预警协议3.2 麻醉方案优化器 4. 验证体系实现4.1 数字孪生验证平台4.2 临床验证流程 5. 系统部署方案5.1 边缘计算架构5.2 性能指标 6. 安…...
MySQL数据库精研之旅第四期:解锁库操作高阶技能
专栏:MySQL数据库成长记 个人主页:手握风云 目录 一、查看所有表 1.1. 语法 二、创建表 2.1. 语法 2.2. 示例 2.3. 表在磁盘上对应的⽂件 三、查看表结构 3.1. 语法 3.2. 示例 四、修改表 4.1. 语法 4.2. 示例 五、删除表 5.1. 语法 5.2.…...
【DevOps】DevOps and CI/CD Pipelines
DevOps 是一种将开发与运维实践相结合的模式,旨在缩短软件开发周期并交付高质量软件。 DevOps 是什么? 开发团队与运维团队之间的协作 • 持续集成与持续交付(CI/CD) • 流程自动化 • 基础设施即代码(IaC)…...
Oracle详解
Oracle 数据库是一款由 Oracle 公司开发和维护的关系数据库管理系统(RDBMS)。Oracle 数据库广泛应用于企业级应用中,尤其是在需要高可用性、高性能和安全性的场景。以下是对 Oracle 数据库的详细介绍,包括它的各个方面。 一、Ora…...
VS自定义静态库并在其他项目中使用
1、VS创建一个空项目或者静态库项目 2、右键项目 属性 修改生成文件类型 3、生成解决方案 4、复制.h文件和.lib文件作为静态库 5、创建一个新项目 测试使用新生成的静态库 在新项目UseStaticLib中加一个新文件夹lib,lib中放入上面的.h和.lib文件。 6、vs中右…...
5G AAU(Active Antenna Unit)详细介绍
5G AAU(Active Antenna Unit)详细介绍 1. 定义与架构 5G AAU(Active Antenna Unit,有源天线单元)是5G无线基站系统中的核心组件,它集成了射频(RF)和天线功能,是4G时代R…...
力扣32.最长有效括号(栈)
32. 最长有效括号 - 力扣(LeetCode) 代码区: #include<stack> #include<string> /*最长有效*/ class Solution { public:int longestValidParentheses(string s) {stack<int> st;int ans0;int ns.length();st.push(-1);fo…...
【计算机网络中的奈氏准则与香农定理】
文章目录 一、前言二、奈氏准则1. 概念2. 奈氏准则公式3. 奈氏准则的意义 三、香农定理1. 概念2. 香农定理公式3. 香农定理的意义 四、奈氏准则与香农定理的对比五、应用示例1. 奈氏准则示例2. 香农定理示例 六、总结 一、前言 在计算机网络中,数据的传输速率与信道…...
vue3 项目中预览 word(.docx)文档方法
vue3 项目中预览 word(.docx)文档方法 通过 vue-office/docx 插件预览 docx 文档通过 vue-office/excel 插件预览 excel 文档通过 vue-office/pdf 插件预览 pdf 文档 安装插件 npm install vue-office/docx vue-demi示例代码 <template><Vu…...
DHCP(Dynamic Host Configuration Protocol)原理深度解析
目录 一、DHCP 核心功能 二、DHCP 工作流程(四阶段) 三、关键技术机制 1. 中继代理(Relay Agent) 2. Option 82(中继信息选项) 3. 租期管理 4. 冲突检测 四、DHCP 与网络架构交互 1. MLAG 环境 2.…...
创建login.api.js步骤和方法
依次创建 login.api.js、home.api.js...... login.api.js、home.api.js 差不多 导入到 main.js main.js 项目中使用...
基于springboot二手交易平台(源码+lw+部署文档+讲解),源码可白嫖!
摘要 人类现已迈入二十一世纪,科学技术日新月异,经济、资讯等各方面都有了非常大的进步,尤其是资讯与网络技术的飞速发展,对政治、经济、军事、文化等各方面都有了极大的影响。 利用电脑网络的这些便利,发展一套二手交…...
帕金森患者的生活重塑:从 “嘴” 开启康复之旅
当提到帕金森病,许多人会联想到震颤、僵硬和行动迟缓等症状。这种神经系统退行性疾病,给患者的生活带来了巨大的挑战。然而,你可知道,帕金森患者恢复正常生活,可以从 “嘴” 开始管理? 帕金森病在全球影响着…...
相生、相克、乘侮、复杂病机及对应的脏腑功能联系
一、五行相生关系(母子关系) 五行生序脏腑关系生理表现举例木生火肝(木)滋养心(火)肝血充足则心血旺盛火生土心(火)温煦脾(土)心阳充足则脾胃运化功能正常土…...
鸿蒙OS 5 架构设计探秘:从分层设计到多端部署
文章目录 鸿蒙OS架构设计探秘:从分层设计到多端部署一、鸿蒙的分层架构设计二、模块化设计的精髓三、智慧分发设计:资源的动态调度四、一次开发,多端部署的实践总结与思考 鸿蒙OS架构设计探秘:从分层设计到多端部署 最近两年来&a…...
5. 实现一个中间件
原文地址: 实现一个中间件 更多内容请关注:php代码框架 理解中间件 中间件(Middleware) 是一种在请求被路由到控制器方法之前或响应返回客户端之前执行的代码。它通常用于处理通用任务,如身份验证、日志记录、CORS 处理等。 在…...
JVM 为什么不使用引用计数算法?——深入解析 GC 策略
在 Java 中,垃圾回收(Garbage Collection, GC)是一个至关重要的功能,它能够自动管理内存,回收不再使用的对象,从而防止内存泄漏。然而,在垃圾回收的实现上,JVM 并未采用引用计数算法…...
【HarmonyOS NEXT】EventHub和Emitter的使用场景与区别
一、EventHub是什么? 移动应用开发的同学应该比较了解EventHub,类似于EventBus。标准的事件广播通知,订阅,取消订阅的处理。EventHub模块提供了事件中心,提供订阅、取消订阅、触发事件的能力。 类似的框架工具有很多…...
01-系统编程
一、程序和进程的区别: window系统: 1、程序存储在硬盘中,文件格式为.exe后缀,静态的 2、进程运行在内存中,动态的 Linux系统 1、程序存储在硬盘中,文件格式为.ELF(可执行的链接文件&#…...
Linux编译器gcc/g++使用完全指南:从编译原理到动静态链接
一、gcc/g基础认知 在Linux开发环境中,gcc和g是我们最常用的编译器工具: gcc:GNU C Compiler,专门用于编译C语言程序g:GNU C Compiler,用于编译C程序(也可编译C语言) 📌…...
UMI-OCR Docker 部署
额外补充 Docker 0.前置条件 部署前,请检查主机的CPU是否具有AVX指令集 lscpu | grep avx 输出如下即可继续部署 Flags: ... avx ... avx2 ... 1.下载dockerfile wget https://raw.githubusercontent.com/hiroi-sora/Umi-OCR_runtime_linux/main/Do…...
26考研|数学分析:定积分及应用
这一部分作为数学分析的灵魂,在数学分析的计算中,绝大部分的问题都可以转换成定积分的计算问题,所以在这部分的学习中,一定要注意提升计算能力,除此之外,由积分引出的相关积分不等式也是分析的重点和难点&a…...
React Hooks使用方法:useState,useRef,useEffect,useReducer,useContext用法实战案例
react hooks介绍,包括了state,ref,effect,reducer,context等常见hooks,也包括forwardRef和createContext用法,下面看代码吧,我用的是js写的。每个hook都做了个案例。 // 使用state来…...
线程池详解:在SpringBoot中的最佳实践
线程池详解:在SpringBoot中的最佳实践 引言 在Java并发编程中,线程池是一种非常重要的资源管理工具,它允许我们在应用程序中有效地管理和重用线程,从而提高性能并降低资源消耗。特别是在SpringBoot等企业级应用中,正…...
扩展卡尔曼滤波
1.非线性系统的线性化 标准卡尔曼滤波 适用于线性化系统,扩展卡尔曼滤波 则扩展到了非线性系统,核心原理就是将非线性系统线性化,主要用的的知识点是 泰勒展开(我另外一篇文章的链接),如下是泰勒展开的公式…...
AI作为学术评审专家有哪些优缺点?
大家好这里是AIWritePaper官方账号,官网👉AIWritePaper论文完成初稿之后,一般情况下,宝子们还需要找专家给我们提出评审意见。找专家评审其实并不容易,即使对老师来说,找人评审论文也是一件苦活。我们这个时…...
微信小程序登录和获取手机号
目录 准备工作 实现流程 实现代码 公共部分 通过code获取openid等信息 解密手机号 扩展 不借助工具类实现解密 借助工具类获取access_token 准备工作 需要小程序账号(可以去微信公众平台创建一个测试号或者正式号) appid:小程序id …...
4.Matplotlib:基础绘图
一 直方图 1.如何构建直方图 将值的范围分段,将整个值的范围分成一系列间隔,然后计算每个间隔中有多少值。 2.直方图的适用场景 一般用横轴表示数据类型,纵轴表示分布情况。 直方图可以用于识别数据的分布模式和异常值,以及观察数…...
与Aspose.pdf类似的jar库分享
如果你在寻找类似于 Aspose.PDF 的 JAR 库,这些库通常用于处理 PDF 文档的创建、编辑、转换、合并等功能。以下是一些类似的 Java 库,它们提供 PDF 处理的功能,其中一些是收费的,但也有开源选项: 1. iText (iText PDF…...
