Java面试宝典:什么是Java中的双亲委派模型?

devtools/2025/3/4 23:27:32/

Java面试宝典专栏范围:JAVA基础,面向对象编程(OOP),异常处理,集合框架,Java I/O,多线程编程,设计模式,网络编程,框架和工具等全方位面试题详解
每日更新Java面试宝典专栏:Java面试宝典
感兴趣的可以先收藏起来,大家在遇到JAVA面试题等相关问题都可以给我留言咨询,希望帮助更多的人

封面图

回答重点

双亲委派模型是Java类加载机制的设计模式之一 。它的核心思想是:类加载器在加载某个类时,会先委派给父类加载器去加载,父类加载器无法加载时,才由当前类加载器自行去加载。

工作流程:

  1. 当前类加载器收到一个类加载的请求
  2. 当前类加载器会将这个请求委托给它的父类加载器去加载
  3. 父类加载器再将请求向上继续委派,直到达到Bootstrap类加载器
  4. 如果Bootstrap类加载器无法加载目标类(即不在其加载范围内),加载请求回到下一级(即扩展类加载器),由它尝试加载类
  5. 如果到达当前类加载器仍然无法加载,则抛ClassNotFountExeption异常

图示

详细解释

三种类加载器

Java 中的类加载器分为以下三类:

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

    • 职责:负责加载 JVM 的核心类库。
    • 加载范围:
      • <JAVA_HOME>/lib 目录,例如 rt.jar
      • -Xbootclasspath 指定的路径下的类。
    • 实现方式:由 C++ 实现,是 JVM 的一部分。
  2. 扩展类加载器(Extension ClassLoader)

    • 职责:加载 JVM 扩展功能的类库。
    • 加载范围:
      • <JAVA_HOME>/lib/ext 目录。
      • 系统属性 java.ext.dirs 指定的路径下的类。
    • 实现方式:由 java.lang.ClassLoader 继承实现。
  3. 应用程序类加载器(Application ClassLoader)

    • 职责:加载用户定义类路径(classpath)下的类。
    • 加载范围:
      • 开发者编写的类和资源文件。
    • 特性:是 Java 中默认的类加载器。若未自定义类加载器,则所有类将由此加载。
委托加载
委托加载
委托加载
加载失败后向下传递
加载失败后向下传递
加载失败后向下传递
自定义ClassLoader
Application ClassLoader
Extension ClassLoader
Bootstrap ClassLoader

为什么要有双亲委派机制?

  1. 避免类的重复加载: 确保同一个类不会被重复加载。当某个类已经被加载到内存中时(例如基础类库中的java.lang.Object,或系统中已有的一些核心类),其他类加载器如果再想加载该类,就会通过双亲委派机制交给已有的类加载器处理,避免重复加载。

  2. 提高安全性: 通过将核心类交由顶层类加载器(如Bootstrap ClassLoader)加载,防止核心API被篡改。这样任何用户自定义的类加载器都无法替代Java的核心类,保护了运行时环境的完整性和安全性。

  3. 保证一致性: Java提供的核心类库(Java API)在任何环境下都应该是一致的,通过双亲委派机制,确保了无论在什么自定义类加载器下,核心类库总是由系统的父类加载器来加载的,这保证了Java应用程序在不同环境下的一致行为。

  4. 简化系统架构: 双亲委派模型简化了Java虚拟机对类及其依赖的管理,使类加载器之间形成一种树状结构,使得系统架构更为简明,减少了类加载器之间的复杂交互。

那你知道有违反双亲委派的例子吗?

典型违反双亲委派的例子就是JDBC
JDBC 的接口是类库定义的,但实现在各大数据库厂商提供的 jar 包中,通过启动类加载器找不到这个实现类,所以需要应用程序加载器完成这个任务,这就违反了自下而上的委托机制。

具体做法是搞了个线程上下文类加载器,通过 setContextClassLoader () 默认设置了应用程序类加载器,然后通过 Thread.current.currentThread ().getContextClassLoader () 获得类加载器来加载。

这是一个具体的例子,实际上 Java 的 SPI 机制都违反了双亲委派模型。因为 SPI 允许开发者在类路径中自定义服务实现,通常通过线程上下文类加载器来加载 SPI 实现类,绕过了父类加载器。

除此之外,在 Java EE 容器(如 Tomcat、WebLogic)中,每个 Web 应用有自己的类加载器,应用级别的类加载器优先加载应用的类库,而不是父类加载器。所以它们也违反了双亲委派。

请你自定义一个类加载器?

继承ClassLoader类冲洗额findClass方法即可实现

java">import java.io.*;public class CustomClassLoader extends ClassLoader {private String classPath;// 构造方法,传入类文件路径public CustomClassLoader(String classPath) {this.classPath = classPath;}// 重写 findClass 方法@Overrideprotected Class<?> findClass(String name) throws ClassNotFoundException {try {// 将类名转换为路径String fileName = classPath + name.replace('.', '/') + ".class";// 读取类文件byte[] classData = loadClassData(fileName);if (classData == null) {throw new ClassNotFoundException("Class file for " + name + " not found");}// 将字节数组转换为 Class 对象return defineClass(name, classData, 0, classData.length);} catch (IOException e) {throw new ClassNotFoundException("Error reading class file for " + name, e);}}// 加载类文件为字节数组private byte[] loadClassData(String fileName) throws IOException {File file = new File(fileName);if (!file.exists()) {return null;}try (InputStream inputStream = new FileInputStream(file);ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {byte[] buffer = new byte[1024];int bytesRead;while ((bytesRead = inputStream.read(buffer)) != -1) {outputStream.write(buffer, 0, bytesRead);}return outputStream.toByteArray();}}public static void main(String[] args) {try {// 指定 .class 文件的路径String classPath = "/path/to/classes/";// 创建自定义类加载器CustomClassLoader customClassLoader = new CustomClassLoader(classPath);// 类的全限定名,例如 "com.example.MyClass"String className = "com.example.MyClass";// 使用自定义类加载器加载类Class<?> loadedClass = customClassLoader.loadClass(className);// 打印加载的类信息System.out.println("Class loaded: " + loadedClass.getName());System.out.println("Class loader: " + loadedClass.getClassLoader());// 调用类的默认构造方法创建一个实例Object instance = loadedClass.getDeclaredConstructor().newInstance();System.out.println("Instance created: " + instance);} catch (Exception e) {e.printStackTrace();}}
}

这这段自定义类加载器代码实现了一个能够从指定路径加载 .class 文件的功能,通过覆写 ClassLoaderfindClass 方法,动态读取类文件并将字节数据转换为 Java 的 Class 对象。构造函数接受类路径,findClass 方法处理类名转路径的转换并加载数据,而 loadClassData 方法则负责读取文件内容。主方法演示了如何使用该类加载器加载并实例化特定类,此设计适用于动态扩展应用功能及处理特殊环境下的类加载需求。

双亲委派机制先自下而上委托,再自上而下加载,那为什么不直接自上而下加载?

双亲委派机制的自上而下加载策略旨在维护Java类加载的安全性和一致性,通过优先委托给父类加载器先加载类,避免了同名类在多个加载器中引发的冲突与不一致性,确保系统核心类(如 java.lang 包)不被恶意修改,从而有效防止类篡改和安全漏洞。此外,该机制通过重用父加载器已加载的类,提高了性能,遵循单一职责原则,从而降低了系统的复杂性与耦合性。这一设计不仅增强了Java运行时环境的稳定性,也为开发者提供了一个更安全可靠的类加载框架。


http://www.ppmy.cn/devtools/164586.html

相关文章

Spark的数据本地性是在哪个环节确定的

首先是关于Sparks数据本地性是在任务调度将诶单的TaskScheduler确定的&#xff0c;巨日就是TaskSc会获取到分区的位置信息&#xff0c;进而确定每个task的最佳执行位置&#xff0c;会由有限将Task分邓培到数据所在节点&#xff0c;进而减少网络传输&#xff0c;如果首选的位置出…

PyTorch 中使用多进程实现增量训练

在 PyTorch 中使用多进程实现增量训练可以提高训练效率,尤其是在处理大规模数据集时。增量训练意味着在已有模型的基础上继续进行训练。以下是实现多进程增量训练的详细步骤和示例代码: 1. 导入必要的库 import torch import torch.nn as nn import torch.optim as optim i…

Windows环境下Maven的配置

Windows环境下Maven的配置 一、Maven下载 Maven官网地址 apache-maven-3.8.8-bin.zip 二、安装和配置 解压到本地目录&#xff0c;例如&#xff1a;D:\software\apache-maven-3.8.8 新建变量MAVEN_HOMED:\software\apache-maven-3.8.8&#xff08;以自己的安装路径为准&…

Excel的两个小问题解决

&#xff08;一&#xff09;因为合并单元格存在&#xff0c;无法使用下拉自动填充公式。 解决方案&#xff1a; 使用 CtrlEnter 组合键 选中目标区域&#xff1a;选中需要应用公式的所有合并单元格区域&#xff0c;这些单元格可能是由 2 行或 3 行等合并而成。输入公式&…

计算机毕业设计SpringBoot+Vue.js基于工程教育认证的计算机课程管理平台(源码+文档+PPT+讲解)

温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 作者简介&#xff1a;Java领…

Metal学习笔记七:片元函数

知道如何通过将顶点数据发送到 vertex 函数来渲染三角形、线条和点是一项非常巧妙的技能 — 尤其是因为您能够使用简单的单行片段函数为形状着色。但是&#xff0c;片段着色器能够执行更多操作。 ➤ 打开网站 https://shadertoy.com&#xff0c;在那里您会发现大量令人眼花缭乱…

P8637 [蓝桥杯 2016 省 B] 交换瓶子

P8637 [蓝桥杯 2016 省 B] 交换瓶子 - 洛谷 题目描述 有 N 个瓶子&#xff0c;编号 1∼N&#xff0c;放在架子上。 比如有 5 个瓶子&#xff1a; markdow 2, 1, 3, 5, 4 要求每次拿起 2 个瓶子&#xff0c;交换它们的位置。 经过若干次后&#xff0c;使得瓶子的序号为&a…

【CSS—前端快速入门】CSS 选择器

CSS 1. CSS介绍 1.1 什么是CSS? CSS(Cascading Style Sheet)&#xff0c;层叠样式表&#xff0c;用于控制页面的样式&#xff1b; CSS 能够对网页中元素位置的排版进行像素级精确控制&#xff0c;实现美化页面的效果&#xff1b;能够做到页面的样式和 结构分离&#xff1b; 1…