博主介绍: ✌博主从事应用安全和大数据领域,有8年研发经验,5年面试官经验,Java技术专家✌

Java知识图谱点击链接:体系化学习Java(Java面试专题)

💕💕 感兴趣的同学可以收藏关注下不然下次找不到哟💕💕

在这里插入图片描述

1、什么是双亲委派机制

双亲委派机制(Parent Delegation Mechanism)是Java中的一种类加载机制。在Java中,类加载器负责加载类的字节码并创建对应的Class对象。双亲委派机制是指当一个类加载器收到类加载请求时,它会先将该请求委派给它的父类加载器去尝试加载。只有当父类加载器无法加载该类时,子类加载器才会尝试加载。

这种机制的设计目的是为了保证类的加载是有序的,避免重复加载同一个类。Java中的类加载器形成了一个层次结构,根加载器(Bootstrap ClassLoader)位于最顶层,它负责加载Java核心类库。其他加载器如扩展类加载器(Extension ClassLoader)和应用程序类加载器(Application ClassLoader)都有各自的加载范围和职责。通过双亲委派机制,可以确保类在被加载时,先从上层的加载器开始查找,逐级向下,直到找到所需的类或者无法找到为止。

这种机制的好处是可以避免类的重复加载,提高了类加载的效率和安全性。同时,它也为Java提供了一种扩展机制,允许开发人员自定义类加载器,实现特定的加载策略。

2、双亲委派机制的优缺点

优点:

  1. 避免重复加载:通过委派给父类加载器,可以避免同一个类被多次加载,提高了加载效率。

  2. 安全性:通过双亲委派机制,核心类库由根加载器加载,可以确保核心类库的安全性,防止恶意代码替换核心类。

  3. 扩展性:开发人员可以自定义类加载器,实现特定的加载策略,从而扩展Java的类加载机制。

缺点:

  1. 灵活性受限:双亲委派机制对于某些特殊的类加载需求可能过于严格,限制了加载器的灵活性。

  2. 破坏隔离性:如果自定义类加载器不遵循双亲委派机制,可能会破坏类加载的隔离性,导致类冲突或安全性问题。

  3. 不适合动态更新:由于类加载器在加载类时会先检查父加载器是否已加载,因此在动态更新类时可能会出现问题,需要额外的处理。

总体而言,双亲委派机制通过层次结构和委派机制提供了一种有序、安全的类加载方式,但也存在一些限制和不适用的情况。

3、双亲委派机制的应用场景

双亲委派机制在Java中有多种应用场景。一些常见的应用场景包括:

  1. 类加载:双亲委派机制主要用于Java中的类加载。它确保类以层次结构方式加载,从引导类加载器开始,然后是扩展类加载器,最后是应用程序类加载器。这样可以高效且一致地加载类,跨类加载器层次结构。

  2. 安全性:双亲委派机制在确保Java应用程序的安全性方面起着重要作用。通过委派类加载给父加载器,它可以防止未经授权或恶意代码的加载和执行。这有助于维护Java运行时环境的完整性和安全性。

  3. 类库:双亲委派机制对于加载和管理类库非常有用。引导类加载器位于层次结构的顶部,负责加载核心Java类。扩展类加载器负责从Java扩展目录加载类。应用程序类加载器负责从应用程序的类路径加载类。这种分离允许高效组织和管理类库。

  4. 自定义类加载:开发人员可以利用双亲委派机制实现具有特定加载行为的自定义类加载器。通过扩展现有的类加载器并重写类加载过程,开发人员可以引入自定义逻辑,从非标准位置加载类或对加载的类应用自定义转换。

双亲委派机制在Java中广泛应用于类加载、安全性强制执行和类库管理等方面,为Java应用程序提供了结构化和安全的环境。

4、双亲委派机制的原理

双亲委派机制是Java中的一种类加载机制。其原理如下:

  1. 当Java程序需要加载一个类时,首先会委托给当前类加载器的父类加载器进行加载。

  2. 父类加载器会按照相同的方式尝试加载该类。如果父类加载器能够成功加载该类,则加载过程结束。

  3. 如果父类加载器无法加载该类,则会将加载请求再次委托给它的父类加载器,直到达到顶层的引导类加载器。

  4. 引导类加载器是Java虚拟机内置的类加载器,它负责加载核心类库,如java.lang包下的类。

  5. 如果引导类加载器也无法加载该类,则会回到初始的类加载器,尝试使用自身的加载机制加载该类。

  6. 如果自身的加载机制仍然无法加载该类,则会抛出ClassNotFoundException异常。

通过这种双亲委派的机制,Java实现了类加载的层次结构。它可以确保类的加载是有序的,避免了重复加载和类的冲突。同时,它也提供了一种安全机制,防止恶意代码的加载和执行。

5、通过代码讲解双亲委派机制

首先,我们需要自定义一个类加载器,继承自ClassLoader类,并重写loadClass()方法。在loadClass()方法中,我们可以实现自己的类加载逻辑。

package com.pany.camp.classloader;

import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.IOException;

/**
 * @description: 双亲委派机制
 * @copyright: @Copyright (c) 2022
 * @company: Aiocloud
 * @author: pany
 * @version: 1.0.0
 * @createTime: 2023-06-30 8:41
 */
public class MyClassLoader extends ClassLoader {
    @Override
    public Class<?> loadClass(String name) throws ClassNotFoundException {
        // 首先尝试使用父类加载器加载类
        try {
            return super.loadClass(name);
        } catch (ClassNotFoundException e) {
            // 如果父类加载器无法加载类,则自己加载类
            return findClass(name);
        }
    }

    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        // 在这里实现自己的类加载逻辑,例如从特定位置加载类文件
        // 这里只是一个示例,实际实现需要根据具体需求进行处理
        byte[] classData = loadClassData(name);
        if (classData == null) {
            throw new ClassNotFoundException();
        }
        return defineClass(name, classData, 0, classData.length);
    }

    private byte[] loadClassData(String name) {
        // 在这里实现加载类文件的逻辑,返回类文件的字节数组
        // 这里只是一个示例,实际实现需要根据具体需求进行处理
        try {
            FileInputStream fis = new FileInputStream(name + ".class");
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            byte[] buffer = new byte[1024];
            int len;
            while ((len = fis.read(buffer)) != -1) {
                bos.write(buffer, 0, len);
            }
            fis.close();
            bos.close();
            return bos.toByteArray();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

    public static void main(String[] args) {
        // 创建自定义类加载器
        MyClassLoader classLoader = new MyClassLoader();

        try {
            // 使用自定义类加载器
            Class<?> clazz = classLoader.loadClass("com.pany.camp.classloader.MyClassLoader");
            System.out.println("Class loaded: " + clazz.getName());
        } catch (ClassNotFoundException e) {
            System.out.println("Class not found");
        }
    }
}

我们创建了一个自定义的类加载器MyClassLoader,并使用它来加载名为"com.example.MyClass"的类。在loadClass()方法中,我们首先尝试使用父类加载器加载类,如果父类加载器无法加载类,则自己加载类。

通过运行上面代码,我们可以观察到双亲委派机制的工作原理。首先,自定义类加载器会尝试委托给父类加载器加载类,如果父类加载器能够成功加载类,则加载过程结束。如果父类加载器无法加载类,则会回到自己的加载机制,尝试使用自身的类加载逻辑加载类。
在这里插入图片描述

💕💕 本文由激流原创,原创不易,感谢支持
💕💕喜欢的话记得点赞收藏啊
在这里插入图片描述

Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐