Java 双亲委派机制:深入理解类加载器的工作原理

引言

在 Java 中,类加载器(ClassLoader)是 JVM 的重要组成部分,负责将类的字节码加载到内存中并生成对应的 Class 对象。Java 的类加载器采用了一种称为 双亲委派机制(Parent Delegation Model) 的设计模式,这种机制不仅保证了类的唯一性,还增强了 Java 程序的安全性和稳定性。本文将深入探讨双亲委派机制的原理、实现及其重要性。


1. 什么是双亲委派机制?

双亲委派机制是 Java 类加载器的一种工作模式,其核心思想是:
当一个类加载器需要加载某个类时,它首先会委托其父类加载器去加载,只有在父类加载器无法加载时,才会尝试自己加载。

这种机制类似于“先问长辈,再自己动手”的逻辑,确保了类的加载过程是有序且安全的。


2. 类加载器的层次结构

在 Java 中,类加载器分为以下几类,它们之间存在层次关系:

  1. Bootstrap ClassLoader(启动类加载器)

    • 最顶层的类加载器,由 C/C++ 实现,负责加载 JVM 核心类库(如 java.lang.*java.util.* 等)。

    • 没有父类加载器。

  2. Extension ClassLoader(扩展类加载器)

    • 负责加载 JAVA_HOME/lib/ext 目录下的类库,或由 java.ext.dirs 系统变量指定的路径。

    • 父类加载器是 Bootstrap ClassLoader。

  3. Application ClassLoader(应用程序类加载器)

    • 也称为系统类加载器(System ClassLoader),负责加载用户类路径(ClassPath)下的类。

    • 父类加载器是 Extension ClassLoader。

  4. 自定义类加载器

    • 开发者可以继承 ClassLoader 类,实现自定义的类加载逻辑。

    • 父类加载器是 Application ClassLoader。


3. 双亲委派机制的工作流程

当一个类加载器收到加载类的请求时,它会按照以下步骤处理:

  1. 委托父类加载器

    • 首先检查是否已经加载过该类,如果已加载则直接返回。

    • 如果没有加载过,则将加载请求委托给父类加载器。

  2. 父类加载器尝试加载

    • 父类加载器重复同样的过程,继续向上委托,直到到达 Bootstrap ClassLoader。

  3. 自上而下尝试加载

    • 如果父类加载器无法加载该类(例如在指定路径下找不到类文件),则由子类加载器尝试加载。

  4. 加载成功或抛出异常

    • 如果某个类加载器成功加载了类,则返回对应的 Class 对象。

    • 如果所有类加载器都无法加载该类,则抛出 ClassNotFoundException


4. 双亲委派机制的代码实现

以下是 ClassLoader 类中双亲委派机制的核心代码(简化版):

protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {synchronized (getClassLoadingLock(name)) {// 1. 检查是否已经加载过该类Class<?> c = findLoadedClass(name);if (c == null) {try {// 2. 委托父类加载器加载if (parent != null) {c = parent.loadClass(name, false);} else {// 3. 如果父类加载器为 null,则委托 Bootstrap ClassLoaderc = findBootstrapClassOrNull(name);}} catch (ClassNotFoundException e) {// 父类加载器无法加载,忽略异常}// 4. 如果父类加载器未加载,则由当前类加载器加载if (c == null) {c = findClass(name);}}// 5. 如果需要解析,则解析该类if (resolve) {resolveClass(c);}return c;}
}

5. 双亲委派机制的优势

1. 避免类的重复加载

  • 通过委派机制,确保每个类只被加载一次,避免了类的重复加载和内存浪费。

2. 保证核心类库的安全性

  • 核心类库(如 java.lang.*)由 Bootstrap ClassLoader 加载,避免了用户自定义类替换核心类的情况,增强了安全性。

3. 保证类的唯一性

  • 同一个类在不同的类加载器中加载会生成不同的 Class 对象,双亲委派机制确保了类的唯一性。


6. 打破双亲委派机制的场景

虽然双亲委派机制是 Java 类加载的默认行为,但在某些场景下需要打破这种机制:

  1. SPI(Service Provider Interface)

    • 例如 JDBC 的 DriverManager,需要加载由第三方实现的驱动类,这些类由 Bootstrap ClassLoader 加载,但 Bootstrap ClassLoader 无法直接加载用户类路径下的类,因此需要打破双亲委派机制。

  2. 热部署

    • 在某些应用服务器(如 Tomcat)中,为了实现热部署,需要为每个 Web 应用提供独立的类加载器。

  3. OSGi 模块化

    • OSGi 框架采用了一种更灵活的类加载机制,允许模块之间的类加载器相互协作。


案例

package java.lang;
public class String {
public String toString(){
return "Hello";
}
public static void main(String[] args){
String s= new String();
s.toString();

这段代码为什么报错?

1. 双亲委派机制回顾

在 Java 中,类加载器采用双亲委派机制来加载类。其核心规则是:

  • 当一个类加载器收到加载类的请求时,它首先会委托其父类加载器去加载。

  • 只有当父类加载器无法加载时,子类加载器才会尝试自己加载。

Java 的核心类库(如 java.lang.String)是由 Bootstrap ClassLoader 加载的,而用户自定义的类通常由 Application ClassLoader 加载。

2. 代码问题分析

问题 1:包名和类名冲突

package java.lang;
public class String {
    // 其他代码
}

  • 问题:你定义了一个 java.lang.String 类,这与 Java 核心类库中的 java.lang.String 类完全冲突。

  • 双亲委派机制的影响

    • 当 JVM 尝试加载 java.lang.String 时,会首先委托 Bootstrap ClassLoader 去加载。

    • Bootstrap ClassLoader 会加载核心类库中的 java.lang.String,而不是你自定义的 String 类。

    • 因此,你的 String 类永远不会被加载,导致后续代码无法正常运行。

问题 2:main 方法中的错误

String s = new String();
s.toString();

  • 问题

    • 这里的 String 是核心类库中的 java.lang.String,而不是你自定义的 String 类。

    • 由于双亲委派机制,JVM 加载的是核心类库中的 String,而不是你定义的 String

    • 因此,s.toString() 调用的是核心类库中 String 类的 toString 方法,而不是你自定义的 toString 方法。

3. 双亲委派机制的具体体现

  1. 类加载过程

    • 当 JVM 遇到 String s = new String(); 时,会尝试加载 java.lang.String 类。

    • 根据双亲委派机制,加载请求会首先委托给 Bootstrap ClassLoader

    • Bootstrap ClassLoader 会加载核心类库中的 java.lang.String,而不是你自定义的 String 类。

  2. 结果

    • 你自定义的 String 类永远不会被加载,因此其中的 toString 方法也不会被调用。

    • 最终,s.toString() 调用的是核心类库中 String 类的 toString 方法,而不是你期望的自定义方法。


  • 双亲委派机制 确保了核心类库的安全性,避免了用户自定义类覆盖核心类库的情况。

  • 不要尝试在 java.lang 包下定义类,尤其是与核心类库同名的类(如 StringInteger 等)。

  • 遵循 Java 的命名规范,避免与核心类库冲突。

7. 总结

双亲委派机制是 Java 类加载器的核心设计,它通过层次化的类加载器结构和委派机制,保证了类的唯一性、安全性和稳定性。理解双亲委派机制不仅有助于我们更好地掌握 Java 的类加载过程,还能帮助我们在实际开发中解决类加载相关的问题。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/24562.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

深度神经网络(DNN)编译器原理简介

深度神经网络(DNN)编译器原理简介 目录 深度神经网络(DNN)编译器原理简介1 什么是DNN编译器2 前端3 后端4 中间表达&#xff08;Intermediate Representation&#xff0c;后文用IR代替&#xff09;5 优化过程6 计算图优化6.1 表达式化简6.2 公共子表达式消除6.3 常数传播6.4 矩…

最小二乘法与梯度下降(原理)

一、最小二乘法 损失函数矩阵形式&#xff1a; 令导数loss0 &#xff0c;可解得&#xff1a; API : sklearn.linear_model.LinearRegression() 功能&#xff1a; 普通最小二乘法线性回归, 权重和偏置是直接算出来的&#xff0c;对于数量大的不适用&#xff0c;因为计算量…

【2025全网最新最全】前端Vue3框架的搭建及工程目录详解

文章目录 安装软件Node.js搭建Vue工程创建Vue工程精简Vue项目文件 Vue工程目录的解读网页标题的设置设置全局样式路由配置 安装软件Node.js 下载地址&#xff1a;https://nodejs.org/zh-cn/ 安装完成后&#xff0c;打开cmd,查看环境是否准备好 node -v npm -vnpm使用之前一定…

Java 之集成 DataX 数据同步工具

1、官网下载 DataX https://github.com/alibaba/DataX 2、将依赖添加到本地&#xff08;DataX没有maven坐标&#xff0c;需要自己安装&#xff09; mvn install:install-file -Dfile"datax-common-0.0.1.jar" "-DgroupIdcom.datax" "-DartifactIdda…

OpenEuler学习笔记(三十五):搭建代码托管服务器

以下是主流的代码托管软件分类及推荐&#xff0c;涵盖自托管和云端方案&#xff0c;您可根据团队规模、功能需求及资源情况选择&#xff1a; 一、自托管代码托管平台&#xff08;可私有部署&#xff09; 1. GitLab 简介: 功能全面的 DevOps 平台&#xff0c;支持代码托管、C…

pikachu

暴力破解 基于表单的暴力破解 【2024版】最新BurpSuit的使用教程&#xff08;非常详细&#xff09;零基础入门到精通&#xff0c;看一篇就够了&#xff01;让你挖洞事半功倍&#xff01;_burpsuite使用教程-CSDN博客 登录页面&#xff0c;随意输入抓包&#xff0c;发送到攻击…

Springboot基础篇(3):Bean管理

前言&#xff1a;Spring 通过扫描类路径&#xff08;Classpath&#xff09;来查找带有特定注解&#xff08;如 Component、Service、Repository 等&#xff09;的类&#xff0c;并将它们注册为 Spring 容器中的 Bean。 1 Bean扫描 Bean 扫描是 Spring 框架的核心功能之一&…

VidSketch:具有扩散控制的手绘草图驱动视频生成

浙大提出的VidSketch是第一个能够仅通过任意数量的手绘草图和简单的文本提示来生成高质量视频动画的应用程序。该方法训练是在单个 RTX4090 GPU 上进行的&#xff0c;针对每个动作类别使用一个小型、高质量的数据集。VidSketch方法使所有用户都能使用简洁的文本提示和直观的手绘…

Vulhub靶机 Apache APISIX Dashboard RCE(CVE-2021-45232)(渗透测试详解)

一、开启vulhub环境 docker-compose up -d 启动docker ps 查看开放的端口 影响范围 2.7 ≤ Apache APISIX Dashboard < 2.10.1 二、访问靶机IP 9080端口 1、下载利用脚本&#xff0c;并利用 https://github.com/wuppp/apisix_dashboard_rce 这里需要注意IP的端口为9000…

Python - Python连接数据库

Python的标准数据库接口为&#xff1a;Python DB-API&#xff0c;Python DB-API为开发人员提供了数据库应用编程接口。 PyMySQL 是在 Python3.x 版本中用于连接 MySQL 服务器的一个实现库&#xff0c;Python2中则使用mysqldb。 PyMySQL 遵循 Python 数据库 API v2.0 规范&…

Windows 11【1001问】Windows 11 都有哪些版本?

随着Windows 11的普及&#xff0c;越来越多的用户开始关注其不同版本及其分支版本之间的差异。在此之前&#xff0c;我们已经通过一系列文章详细介绍了Windows 11的基本概念、硬件配置要求、系统镜像下载方法以及多种安装方式。从使用Rufus和UltraISO软碟通制作Windows 11系统安…

【Kimi】自动生成PPT-并支持下载和在线编辑--全部免费

【Kimi】免费生成PPT并免费下载 用了好几个大模型&#xff0c;有些能生成PPT内容&#xff1b; 有些能生成PPT&#xff0c;但下载需要付费&#xff1b; 目前只有Kimi生成的PPT&#xff0c;能选择模板、能在线编辑、能下载&#xff0c;关键全部免费&#xff01; 一、用kimi生成PP…

【Java项目】基于Spring Boot的旅游管理系统

【Java项目】基于Spring Boot的旅游管理系统 技术简介&#xff1a;采用Java技术、Spring Boot框架、MySQL数据库等实现。 系统简介&#xff1a;旅游管理系统是一个基于Web的在线平台&#xff0c;主要分为前台和后台两大功能模块。前台功能模块包括&#xff08;1&#xff09;首…

Deepseek开源周第三天:DeepGEMM发布

Deepseek开源周第三天&#xff1a;DeepGEMM发布 前言 上周deepseek宣布&#xff0c;将在本周陆续发布五个开源项目&#xff0c;这些库已经在生产环境中经过了记录、部署和实战测试。 今天是deepseek开源周的第三天&#xff0c;deepseek发布了一个名为 DeepGEMM 的项目&#x…

ALM研发管理:全新甘特图,让项目管理更高效

在软件开发领域&#xff0c;甘特图一直是项目管理的重要工具。通过可视化的任务时间线&#xff0c;清晰地展示项目的进度和关键时间节点&#xff0c;帮助团队成员快速理解项目状态&#xff0c;协调工作进度&#xff0c;从而有效提升项目管理的效率。无论是需求分析、设计、开发…

mac os 使用 root 登录

打开系统偏好设置。进入“用户与群组”面板。点按锁按钮输入密码&#xff0c;再点最下面的登录选项。在右边面板的下方你会看见一行字&#xff1a;”网络账户服务器&#xff1a;加入 ”&#xff0c;点击加入&#xff0c;就可以打开目录实用工具了。 添加Root用户 正常情况下目…

【C++笔记】C++11智能指针的使用及其原理

【C笔记】C11智能指针的使用及其原理 &#x1f525;个人主页&#xff1a;大白的编程日记 &#x1f525;专栏&#xff1a;C笔记 文章目录 【C笔记】C11智能指针的使用及其原理前言1.智能指针的使用场景分析2. RAII和智能指针的设计思路3. C标准库智能指针的使用4. 智能指针的原…

SQL命令详解之操作数据库

操作数据库 SQL是用于管理和操作关系型数据库的标准语言。数据库操作是SQL的核心功能之一&#xff0c;主要用于创建、修改和删除数据库对象&#xff0c;如数据库、表、视图和索引等。以下是SQL中常见的数据库操作命令及其功能简介&#xff1a; 1. 查询数据库 查询所有的数据库…

轨迹控制--odrive的位置控制---负载设置

轨迹控制 此模式使您可以平滑地使电机旋转&#xff0c;从一个位置加速&#xff0c;匀速和减速到另一位置。 使用位置控制时&#xff0c;控制器只是试图尽可能快地到达设定点。 使用轨迹控制模式可以使您更灵活地调整反馈增益&#xff0c;以消除干扰&#xff0c;同时保持平稳的运…

mysql.gtid_executed表、gtid_executed变量、gtid_purged变量的修改时机

1.2 mysql.gtid_executed表、gtid_executed变量、gtid_purged变量的修改时机 1.2.1 定义 mysql.gtid_executed表&#xff1a;GTID持久化的介质&#xff0c;GTID模块初始化的时候会读取这个表作为获取gtid_executed变量的基础。 gtid_executed变量&#xff1a;表示数据库中执行…