深入理解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. 自定义类加载器的应用场景
自定义类加载器主要用于处理标准类加载器无法满足的需求,如加载网络上的类文件、动态生成的类、加密后的类文件等。通过自定义类加载器,可以灵活地控制类的加载行为,满足多样化的应用场景。
推荐阅读
-
本文详细探讨了JDBC(Java数据库连接)的内部机制,重点分析其作为服务提供者接口(SPI)框架的应用。通过类图和代码示例,展示了JDBC如何注册驱动程序、建立数据库连接以及执行SQL查询的过程。 ...
[详细]
蜡笔小新 2024-12-25 19:59:15
-
本文旨在为读者提供对Java虚拟机(JVM)的全面理解,涵盖其主要组成部分、工作原理及其在不同平台上的实现。通过详细探讨JVM的结构和内部机制,帮助开发者更好地掌握Java编程的核心技术。 ...
[详细]
蜡笔小新 2024-12-21 23:50:40
-
-
本文深入探讨了 Spring Boot 如何简化 Web 应用中的静态资源管理,包括默认的静态资源映射规则、WebJars 的使用以及静态首页的处理方法。通过本文,您将了解如何高效地管理和引用静态资源。 ...
[详细]
蜡笔小新 2024-12-20 20:02:31
-
1:有如下一段程序:packagea.b.c;publicclassTest{privatestaticinti0;publicintgetNext(){return ...
[详细]
蜡笔小新 2024-12-27 19:32:17
-
本文详细介绍了如何通过HTTP响应和请求处理浏览器的Cookie信息,以及如何创建、设置和管理Cookie。同时探讨了会话跟踪技术中的Session机制,解释其原理及应用场景。 ...
[详细]
蜡笔小新 2024-12-27 18:20:43
-
主要用了2个类来实现的,话不多说,直接看运行结果,然后在奉上源代码1.Index.javaimportjava.awt.Color;im ...
[详细]
蜡笔小新 2024-12-27 18:18:10
-
本文详细介绍了Java中org.eclipse.ui.forms.widgets.ExpandableComposite类的addExpansionListener()方法,并提供了多个实际代码示例,帮助开发者更好地理解和使用该方法。这些示例来源于多个知名开源项目,具有很高的参考价值。 ...
[详细]
蜡笔小新 2024-12-27 16:11:49
-
本文深入探讨了 Java 中的 Serializable 接口,解释了其实现机制、用途及注意事项,帮助开发者更好地理解和使用序列化功能。 ...
[详细]
蜡笔小新 2024-12-27 15:06:12
-
本文详细介绍了Akka中的BackoffSupervisor机制,探讨其在处理持久化失败和Actor重启时的应用。通过具体示例,展示了如何配置和使用BackoffSupervisor以实现更细粒度的异常处理。 ...
[详细]
蜡笔小新 2024-12-27 15:04:09
-
本文介绍如何在Java项目中使用Log4j库进行日志记录。我们将详细说明Log4j库的引入、配置及简单应用,帮助开发者快速上手。 ...
[详细]
蜡笔小新 2024-12-26 10:59:04
-
在处理Java项目中的JAR包冲突时,我们遇到了JaxWsDynamicClientFactory被弃用的问题,并成功将其迁移到org.codehaus.xfire.client。本文详细介绍了这一过程及解决方案。 ...
[详细]
蜡笔小新 2024-12-25 18:48:34
-
本文详细介绍了如何将Struts和Spring两个流行的Java Web开发框架进行整合,涵盖从环境配置到代码实现的具体步骤。 ...
[详细]
蜡笔小新 2024-12-23 17:46:59
-
本文详细介绍了EasyReport,一个易于使用的开源Web报表工具。该工具支持Hadoop、HBase及多种关系型数据库,能够将SQL查询结果转换为HTML表格,并提供Excel导出、图表显示和表头冻结等功能。 ...
[详细]
蜡笔小新 2024-12-22 11:11:28
-
ssm框架整合及工程分层1.先创建一个新的project1.1配置pom.xml ...
[详细]
蜡笔小新 2024-12-21 12:56:58
-
1一定要使用slf4j的jar包,不要使用apachecommons的jar。否则滚动生成文件不生效,不滚动的时候却生效~~importorg.slf ...
[详细]
蜡笔小新 2024-12-19 20:40:37
-