热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

深入理解Java类加载机制与自定义类加载器

本文详细探讨了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查询的过程。 ... [详细]
  • 深入解析Java虚拟机(JVM)架构与原理
    本文旨在为读者提供对Java虚拟机(JVM)的全面理解,涵盖其主要组成部分、工作原理及其在不同平台上的实现。通过详细探讨JVM的结构和内部机制,帮助开发者更好地掌握Java编程的核心技术。 ... [详细]
  • Spring Boot 中静态资源映射详解
    本文深入探讨了 Spring Boot 如何简化 Web 应用中的静态资源管理,包括默认的静态资源映射规则、WebJars 的使用以及静态首页的处理方法。通过本文,您将了解如何高效地管理和引用静态资源。 ... [详细]
  • 1:有如下一段程序:packagea.b.c;publicclassTest{privatestaticinti0;publicintgetNext(){return ... [详细]
  • 深入理解Cookie与Session会话管理
    本文详细介绍了如何通过HTTP响应和请求处理浏览器的Cookie信息,以及如何创建、设置和管理Cookie。同时探讨了会话跟踪技术中的Session机制,解释其原理及应用场景。 ... [详细]
  • 主要用了2个类来实现的,话不多说,直接看运行结果,然后在奉上源代码1.Index.javaimportjava.awt.Color;im ... [详细]
  • 本文详细介绍了Java中org.eclipse.ui.forms.widgets.ExpandableComposite类的addExpansionListener()方法,并提供了多个实际代码示例,帮助开发者更好地理解和使用该方法。这些示例来源于多个知名开源项目,具有很高的参考价值。 ... [详细]
  • 本文深入探讨了 Java 中的 Serializable 接口,解释了其实现机制、用途及注意事项,帮助开发者更好地理解和使用序列化功能。 ... [详细]
  • 本文详细介绍了Akka中的BackoffSupervisor机制,探讨其在处理持久化失败和Actor重启时的应用。通过具体示例,展示了如何配置和使用BackoffSupervisor以实现更细粒度的异常处理。 ... [详细]
  • 本文介绍如何在Java项目中使用Log4j库进行日志记录。我们将详细说明Log4j库的引入、配置及简单应用,帮助开发者快速上手。 ... [详细]
  • 解决JAX-WS动态客户端工厂弃用问题并迁移到XFire
    在处理Java项目中的JAR包冲突时,我们遇到了JaxWsDynamicClientFactory被弃用的问题,并成功将其迁移到org.codehaus.xfire.client。本文详细介绍了这一过程及解决方案。 ... [详细]
  • Struts与Spring框架的集成指南
    本文详细介绍了如何将Struts和Spring两个流行的Java Web开发框架进行整合,涵盖从环境配置到代码实现的具体步骤。 ... [详细]
  • 简化报表生成:EasyReport工具的全面解析
    本文详细介绍了EasyReport,一个易于使用的开源Web报表工具。该工具支持Hadoop、HBase及多种关系型数据库,能够将SQL查询结果转换为HTML表格,并提供Excel导出、图表显示和表头冻结等功能。 ... [详细]
  • ssm框架整合及工程分层1.先创建一个新的project1.1配置pom.xml ... [详细]
  • Logback使用小结
    1一定要使用slf4j的jar包,不要使用apachecommons的jar。否则滚动生成文件不生效,不滚动的时候却生效~~importorg.slf ... [详细]
author-avatar
尚庆湃
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有