深入理解Java类加载机制与自定义类加载器
作者:尚庆湃 | 来源:互联网 | 2024-12-20 12:58
本文详细探讨了Java中的ClassLoader类加载器的工作原理,包括其如何将class文件加载至JVM中,以及JVM启动时的动态加载策略。文章还介绍了JVM内置的三种类加载器及其工作方式,并解释了类加载器的继承关系和双亲委托机制。
### 1. 类加载器的基本概念
ClassLoader的主要职责是将class文件加载到JVM中。当JVM启动时,并不是一次性加载所有类,而是根据需要动态加载。这种加载方式主要分为隐式加载和显式加载。
- **隐式加载**:程序运行时,如果需要某个类但该类尚未被加载到内存中,JVM会自动将其加载。例如,当一个类继承或引用另一个类时,若后者未被加载,则JVM会自动加载。
- **显式加载**:通过编程方式明确指定加载某个类,如使用`Class.forName()`、`this.getClass().getClassLoader().loadClass()`等方法。
### 2. JVM内置的类加载器
JVM提供了三种主要的类加载器,每种负责不同的类加载任务:
- **启动类加载器(Bootstrap ClassLoader)**:负责加载JRE的核心类库,如`rt.jar`、`resources.jar`等。这些类通常位于`%JRE_HOME%\lib`目录下。可以通过`System.getProperty("sun.boot.class.path")`查看具体路径。
- **扩展类加载器(Extension ClassLoader)**:负责加载`%JRE_HOME%\lib\ext`目录下的扩展类库。路径可通过`System.getProperty("java.ext.dirs")`获取。
- **应用类加载器(Application ClassLoader)**:负责加载应用程序类路径(classpath)下的类。这是开发者最常接触的类加载器。
### 3. 类加载器的继承关系
这三个类加载器之间存在明确的继承关系:
- `ExtClassLoader`和`AppClassLoader`都继承自`URLClassLoader`,而`URLClassLoader`又继承自`ClassLoader`。
- `Bootstrap ClassLoader`由C/C++编写,是JVM的一部分,不直接继承自任何Java类。
加载顺序遵循从上到下的原则:`Bootstrap ClassLoader` -> `ExtClassLoader` -> `AppClassLoader`。
### 4. 双亲委托机制
双亲委托机制是Java类加载的一个重要特性,确保类的加载具有全局唯一性。当一个类加载器收到类加载请求时,它首先不会自己尝试加载这个类,而是把这个请求委派给父类加载器完成,每一层的类加载器都是如此,因此所有的加载请求最终都应该传送到顶层的启动类加载器中。只有当父加载器反馈自己无法完成加载请求(它的搜索范围中没有找到所需的类)时,子加载器才会尝试自己去加载。
### 5. 类加载过程的关键方法
类加载过程中涉及几个核心方法,包括`loadClass`、`findLoadedClass`和`findClass`:
- `loadClass`:处理类的加载逻辑,包括检查是否已加载、委托给父加载器、调用`findClass`等。
- `findLoadedClass`:检查类是否已经被加载。
- `findClass`:如果类未被加载,此方法负责查找并加载类。
### 6. 实现自定义类加载器
为了满足特定需求,如加载网络上的类文件或加密后的类文件,可以实现自定义类加载器。基本步骤包括:
1. 继承`ClassLoader`类。
2. 重写`findClass`方法,实现自定义的类查找逻辑。
3. 使用`defineClass`方法将字节码转换为类对象。
#### 示例代码
```java
package test;
import java.io.*;
public class MyClassLoader extends ClassLoader {
private String classpath;
public MyClassLoader(String classpath) {
this.classpath = classpath;
}
@Override
protected Class> findClass(String name) throws ClassNotFoundException {
byte[] classData = getClassData(name);
if (classData == null) {
throw new ClassNotFoundException();
} else {
return defineClass(name, classData, 0, classData.length);
}
}
private byte[] getClassData(String className) {
String path = classpath + File.separatorChar + className.replace('.', File.separatorChar) + ".class";
try (InputStream in = new FileInputStream(path); ByteArrayOutputStream out = new ByteArrayOutputStream()) {
byte[] buffer = new byte[2048];
int len;
while ((len = in.read(buffer)) != -1) {
out.write(buffer, 0, len);
}
return out.toByteArray();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
}
```
#### 测试代码
```java
package test;
import java.lang.reflect.Method;
public class TestMyClassLoader {
public static void main(String[] args) throws Exception {
MyClassLoader myClassLoader = new MyClassLoader("D:\\lib");
Class> c = myClassLoader.loadClass("com.test.Test");
if (c != null) {
Object obj = c.newInstance();
Method method = c.getMethod("say");
method.invoke(obj);
System.out.println(c.getClassLoader());
}
}
}
```
### 7. 自定义类加载器的应用场景
自定义类加载器主要用于处理标准类加载器无法满足的需求,如加载网络上的类文件、动态生成的类、加密后的类文件等。通过自定义类加载器,可以灵活地控制类的加载行为,满足多样化的应用场景。
推荐阅读
-
代理模式是 Java 中常用的设计模式之一,其核心在于代理类与委托类共享相同的接口。代理类主要用于为委托类提供预处理、过滤、转发及后处理等功能,以增强或改变原有功能的行为。 ...
[详细]
蜡笔小新 2024-12-17 18:09:39
-
本报告详细记录了2017-2018学年《网络编程与安全》课程第五次实验的具体内容、实验过程、遇到的问题及解决方案。 ...
[详细]
蜡笔小新 2024-12-20 08:38:38
-
-
本文详细探讨了Java虚拟机(JVM)中类加载器的工作原理,特别是如何通过类的全限定名从外部源获取二进制字节流,以及不同类型的类加载器及其在双亲委派模型中的角色。 ...
[详细]
蜡笔小新 2024-12-12 13:15:46
-
本文将详细探讨 Java 中提供的不可变集合(如 `Collections.unmodifiableXXX`)和同步集合(如 `Collections.synchronizedXXX`)的实现原理及使用方法,帮助开发者更好地理解和应用这些工具。 ...
[详细]
蜡笔小新 2024-12-20 15:34:31
-
本文探讨了如何在Java中使用JAXB解组两个具有相同名称但不同结构的对象。我们将介绍一个抽象类Bar及其具体实现,并展示如何正确地解析XML文档以获取正确的对象实例。 ...
[详细]
蜡笔小新 2024-12-20 12:22:08
-
在使用 Spring Cloud Config 作为配置中心时,若在配置文件中指定了请求路径但未能生效,本文将探讨其原因及解决方案。 ...
[详细]
蜡笔小新 2024-12-15 13:22:47
-
本文深入探讨了Java中的代理模式,包括静态代理和动态代理的概念、实现及其应用场景。通过具体的代码示例,详细解析了如何利用代理模式增强代码的功能性和灵活性。 ...
[详细]
蜡笔小新 2024-12-14 11:56:27
-
随着毕业设计的结束,我终于有时间更新我的博客了。这次,我将分享如何在自己的服务器上搭建 Bitwarden,一个广受好评的开源密码管理工具。 ...
[详细]
蜡笔小新 2024-12-12 22:06:57
-
本文探讨了如何利用HTML5和JavaScript在浏览器中进行本地文件的读取和写入操作,并介绍了获取本地文件路径的方法。HTML5提供了一系列API,使得这些操作变得更加简便和安全。 ...
[详细]
蜡笔小新 2024-12-20 18:36:06
-
本文深入探讨了 Asp.net MVC 中如何利用 Bundle 功能来合并和压缩 JavaScript 和 CSS 文件,提供了详细的配置步骤和示例代码,适合开发人员参考学习。 ...
[详细]
蜡笔小新 2024-12-18 18:54:11
-
目录一、安装JDK(需要安装依赖javaJDK)二、安装Kafka三、测试参考在Windows系统上安装消息队列kafka一、安装JDKÿ ...
[详细]
蜡笔小新 2024-12-16 12:01:36
-
本文探讨了使用Filter作为控制器的优势,以及Servlet与Filter之间的主要差异。同时,详细解析了Servlet的工作流程及其生命周期,以及ServletConfig与ServletContext的区别与应用场景。 ...
[详细]
蜡笔小新 2024-12-16 06:11:11
-
本文介绍了如何在Ubuntu 16.04系统上配置Nginx服务器,以便能够通过网络访问存储在服务器上的图片资源。这解决了在网页开发中需要使用自定义在线图标的需求。 ...
[详细]
蜡笔小新 2024-12-12 16:35:25
-
了解如何快速搭建属于自己的个人博客,无需编程基础,适合Mac和Windows用户。通过本文,您将学会使用GitHub Pages和Hexo构建一个完全自主的在线空间。 ...
[详细]
蜡笔小新 2024-12-14 03:24:08
-
本文探讨了在PHP开发过程中遇到的时间获取错误问题,并提供了详细的解决方案,包括通过修改配置文件和编程方法来调整时区设置。 ...
[详细]
蜡笔小新 2024-12-13 15:33:22
-