博主介绍: ✌博主从事应用安全和大数据领域,有8年研发经验,5年面试官经验,Java技术专家,阿里云专家博主,华为云云享专家✌
💕💕 感兴趣的同学可以收藏关注下 ,不然下次找不到哟💕💕
1、类加载器的定义
类加载器(ClassLoader)是Java虚拟机(JVM)的一个重要组成部分,负责将类的字节码加载到内存中,并转换成可执行的Java类。类加载器是实现Java的动态性和灵活性的基础。
类加载器的主要职责包括:
- 加载:查找并加载类的字节码文件到内存中。
- 链接:将类的字节码文件进行链接,包括验证、准备和解析等步骤。
- 初始化:对类进行初始化,包括执行静态变量赋值和静态代码块等操作。
- 缓存:缓存已加载的类,避免重复加载。
Java中的类加载器采用了双亲委派模型,即每个类加载器在加载类时,首先将加载请求委派给父类加载器,只有在父类加载器无法加载时,才由当前类加载器自己加载。这种机制保证了类的加载顺序和一致性,防止类的重复加载。
Java中的类加载器分为以下几种类型:
- 启动类加载器(Bootstrap ClassLoader):负责加载JVM核心类库,如rt.jar等。
- 扩展类加载器(Extension ClassLoader):负责加载Java的扩展库,如jre/lib/ext目录下的jar包。
- 应用程序类加载器(Application ClassLoader):也称为系统类加载器,负责加载应用程序的类路径(classpath)下的类。
- 自定义类加载器(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类并重写其中的方法,来实现自己的类加载器。通过自定义类加载器,开发者可以实现一些特定的类加载逻辑,例如加载非标准的类文件格式、从非传统的数据源加载类等。
自定义类加载器的实现通常需要重写以下方法:
- findClass(String name):根据类的名称加载类的字节码,可以通过读取文件、网络请求等方式获取字节码。
- defineClass(String name, byte[] b, int off, int len):将字节码转换为Class对象。
- loadClass(String name, boolean resolve):加载类,如果父类加载器无法加载,则调用自定义的findClass方法进行加载。
- findResource(String name):根据资源的名称查找资源文件。
- getResource(String name):获取资源文件的URL。
- 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
💕💕 本文由激流原创,原创不易,感谢支持 💕💕喜欢的话记得点赞收藏啊