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

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

在这里插入图片描述

1、类加载器的定义

类加载器(ClassLoader)是Java虚拟机(JVM)的一个重要组成部分,负责将类的字节码加载到内存中,并转换成可执行的Java类。类加载器是实现Java的动态性和灵活性的基础。

类加载器的主要职责包括:

  1. 加载:查找并加载类的字节码文件到内存中。
  2. 链接:将类的字节码文件进行链接,包括验证、准备和解析等步骤。
  3. 初始化:对类进行初始化,包括执行静态变量赋值和静态代码块等操作。
  4. 缓存:缓存已加载的类,避免重复加载。

Java中的类加载器采用了双亲委派模型,即每个类加载器在加载类时,首先将加载请求委派给父类加载器,只有在父类加载器无法加载时,才由当前类加载器自己加载。这种机制保证了类的加载顺序和一致性,防止类的重复加载。

Java中的类加载器分为以下几种类型:

  1. 启动类加载器(Bootstrap ClassLoader):负责加载JVM核心类库,如rt.jar等。
  2. 扩展类加载器(Extension ClassLoader):负责加载Java的扩展库,如jre/lib/ext目录下的jar包。
  3. 应用程序类加载器(Application ClassLoader):也称为系统类加载器,负责加载应用程序的类路径(classpath)下的类。
  4. 自定义类加载器(Custom ClassLoader):用户可以根据需要自定义类加载器,实现自己的类加载逻辑。

类加载器是Java语言的重要特性之一,它为Java提供了灵活的动态性和扩展性。通过自定义类加载器,我们可以实现一些特殊的类加载需求,如热部署、类隔离等。

类加载器之间的关系: 在这里插入图片描述

2、启动类加载器

启动类加载器(Bootstrap ClassLoader)是Java虚拟机(JVM)中最顶层的类加载器,负责加载JVM核心类库,如rt.jar等。它是由JVM自身实现的,通常使用C++编写。启动类加载器是所有其他类加载器的父加载器,它没有父加载器。

启动类加载器负责加载JVM运行时所需的核心类库,这些类库包括Java的基础类库和一些关键的运行时类。这些类库通常位于JVM的安装目录下,如jre/lib目录下的核心类库。启动类加载器是JVM的一部分,它在JVM启动时就会被创建并初始化,不受Java应用程序的控制。

启动类加载器是Java类加载器体系中唯一一个不是Java类的加载器,它是由JVM实现的,具体实现方式因不同的JVM而有所差异。启动类加载器主要负责加载JVM运行时所需的核心类库,为Java应用程序的正常运行提供基础支持。

由于启动类加载器是JVM的一部分,无法通过Java代码直接访问或修改其行为。它的存在是为了保证JVM的稳定和安全运行,确保核心类库的加载和使用符合JVM的规范和要求。

3、扩展类加载器

扩展类加载器(Extension ClassLoader)是Java虚拟机(JVM)中的一个类加载器,它负责加载Java应用程序的扩展类库。扩展类库是指位于JRE的ext目录下的JAR文件,这些JAR文件包含了一些由第三方开发的扩展功能或库。扩展类加载器是在启动类加载器之后,应用类加载器之前被创建的。

扩展类加载器是Java的标准类加载器之一,它继承自java.lang.ClassLoader类。它的主要职责是从JRE的ext目录下加载扩展类库,并将其添加到Java应用程序的类路径中,从而使得应用程序可以使用这些扩展类库中的类和资源。

扩展类加载器的代码实现通常是由JVM提供的,具体实现方式可能因不同的JVM而有所差异。扩展类加载器在加载扩展类库时会遵循双亲委派模型,首先尝试由启动类加载器加载,如果找不到则由扩展类加载器加载,如果还找不到则由应用类加载器加载。

扩展类加载器的加载路径可以通过系统属性"java.ext.dirs"来指定,它默认为JRE的ext目录。开发者也可以通过自定义扩展类加载器来加载其他位置的扩展类库。

需要注意的是,扩展类加载器只负责加载扩展类库,而不负责加载应用程序的类。应用程序的类加载由应用类加载器负责。

4、系统类加载器

系统类加载器(System ClassLoader),也称为应用类加载器(Application ClassLoader),是Java虚拟机(JVM)中的一个类加载器,负责加载Java应用程序的类和资源。系统类加载器是在启动类加载器和扩展类加载器之后被创建的,它是类加载器的最后一个级别。

系统类加载器是Java的标准类加载器之一,它继承自java.lang.ClassLoader类。它的主要职责是从应用程序的类路径中加载类和资源。应用程序的类路径可以通过系统属性"java.class.path"来指定,它包括了应用程序的代码和依赖的类库。

系统类加载器在加载类时会遵循双亲委派模型,首先尝试由启动类加载器加载,然后由扩展类加载器加载,最后由系统类加载器加载。如果都找不到该类,则会抛出ClassNotFoundException异常。 系统类加载器的代码实现通常是由JVM提供的,具体实现方式可能因不同的JVM而有所差异。开发者也可以通过 自定义类加载器来扩展系统类加载器的功能。

需要注意的是,系统类加载器只负责加载应用程序的类和资源,不负责加载JVM核心类库和扩展类库,这是由启动类加载器和扩展类加载器负责的。

5、 自定义类加载器

自定义类加载器是指开发者根据自己的需求,通过继承java.lang.ClassLoader类并重写其中的方法,来实现自己的类加载器。通过自定义类加载器,开发者可以实现一些特定的类加载逻辑,例如加载非标准的类文件格式、从非传统的数据源加载类等。

自定义类加载器的实现通常需要重写以下方法:

  1. findClass(String name):根据类的名称加载类的字节码,可以通过读取文件、网络请求等方式获取字节码。
  2. defineClass(String name, byte[] b, int off, int len):将字节码转换为Class对象。
  3. loadClass(String name, boolean resolve):加载类,如果父类加载器无法加载,则调用自定义的findClass方法进行加载。
  4. findResource(String name):根据资源的名称查找资源文件。
  5. getResource(String name):获取资源文件的URL。
  6. getResourceAsStream(String name):获取资源文件的输入流。

自定义类加载器可以用于实现一些特定的需求,例如实现热部署功能、实现类的加密和解密、实现类的版本控制等。但需要注意的是,自定义类加载器的实现需要遵循类加载器的双亲委派模型,即先尝试由父类加载器加载类,只有在父类加载器无法加载时才由自定义类加载器加载。这样可以确保类的加载过程是安全和可靠的。

自定义一个测试类

package com.pany.camp.classloader;

/**
 *
 * @description:  自定义测试类
 * @copyright: @Copyright (c) 2022
 * @company: Aiocloud
 * @author: pany
 * @version: 1.0.0
 * @createTime: 2023-06-30 20:05
 */
public class MyClass {

    public MyClass() {
    }

    public void printName() {
        System.out.println("自定义类加载器");
    }
}

自定义一个类加载器

package com.pany.camp.classloader;

import java.io.*;

/**
 *
 * @description:  自定义类加载器
 * @copyright: @Copyright (c) 2022
 * @company: Aiocloud
 * @author: pany
 * @version: 1.0.0
 * @createTime: 2023-06-30 20:06
 */
public class CustomClassLoader extends ClassLoader {
    private String path; // 类文件所在的路径

    public CustomClassLoader(String path) {
        this.path = path;
    }

    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        byte[] data = loadClassData(name);
        return defineClass(name, data, 0, data.length);
    }

    private byte[] loadClassData(String name) throws ClassNotFoundException {
        try {
            String fileName = name.replace(".", File.separator) + ".class";
            FileInputStream fis = new FileInputStream(new File(path + fileName));
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            int len;
            while ((len = fis.read()) != -1) {
                bos.write(len);
            }
            fis.close();
            return bos.toByteArray();
        } catch (IOException e) {
            throw new ClassNotFoundException("Class not found: " + name, e);
        }
    }

    public static void main(String[] args) throws Exception {
        CustomClassLoader classLoader = new CustomClassLoader("D:\\software\\idea\\codeSpace\\sourceCode\\java-study\\src\\main\\java\\com\\pany\\camp\\classloader");
        Class<?> clazz = classLoader.loadClass("com.pany.camp.classloader.MyClass");
        Object obj = clazz.newInstance();
        // 调用MyClass的方法
        clazz.getMethod("printName").invoke(obj);
    }
}

输出结果如下:

Connected to the target VM, address: '127.0.0.1:49761', transport: 'socket'
自定义类加载器
Disconnected from the target VM, address: '127.0.0.1:49761', transport: 'socket'

Process finished with exit code 0

在这里插入图片描述

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