热门标签 | 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查询的过程。 ... [详细]
  • 本文将介绍如何编写一些有趣的VBScript脚本,这些脚本可以在朋友之间进行无害的恶作剧。通过简单的代码示例,帮助您了解VBScript的基本语法和功能。 ... [详细]
  • 优化局域网SSH连接延迟问题的解决方案
    本文介绍了解决局域网内SSH连接到服务器时出现长时间等待问题的方法。通过调整配置和优化网络设置,可以显著缩短SSH连接的时间。 ... [详细]
  • 深入解析Java虚拟机(JVM)架构与原理
    本文旨在为读者提供对Java虚拟机(JVM)的全面理解,涵盖其主要组成部分、工作原理及其在不同平台上的实现。通过详细探讨JVM的结构和内部机制,帮助开发者更好地掌握Java编程的核心技术。 ... [详细]
  • Spring Boot 中静态资源映射详解
    本文深入探讨了 Spring Boot 如何简化 Web 应用中的静态资源管理,包括默认的静态资源映射规则、WebJars 的使用以及静态首页的处理方法。通过本文,您将了解如何高效地管理和引用静态资源。 ... [详细]
  • 本文详细介绍如何使用Python进行配置文件的读写操作,涵盖常见的配置文件格式(如INI、JSON、TOML和YAML),并提供具体的代码示例。 ... [详细]
  • 技术分享:从动态网站提取站点密钥的解决方案
    本文探讨了如何从动态网站中提取站点密钥,特别是针对验证码(reCAPTCHA)的处理方法。通过结合Selenium和requests库,提供了详细的代码示例和优化建议。 ... [详细]
  • Linux 系统启动故障排除指南:MBR 和 GRUB 问题
    本文详细介绍了 Linux 系统启动过程中常见的 MBR 扇区和 GRUB 引导程序故障及其解决方案,涵盖从备份、模拟故障到恢复的具体步骤。 ... [详细]
  • 本文探讨了Hive中内部表和外部表的区别及其在HDFS上的路径映射,详细解释了两者的创建、加载及删除操作,并提供了查看表详细信息的方法。通过对比这两种表类型,帮助读者理解如何更好地管理和保护数据。 ... [详细]
  • 本文详细介绍了如何在Linux系统上安装和配置Smokeping,以实现对网络链路质量的实时监控。通过详细的步骤和必要的依赖包安装,确保用户能够顺利完成部署并优化其网络性能监控。 ... [详细]
  • 深入理解Tornado模板系统
    本文详细介绍了Tornado框架中模板系统的使用方法。Tornado自带的轻量级、高效且灵活的模板语言位于tornado.template模块,支持嵌入Python代码片段,帮助开发者快速构建动态网页。 ... [详细]
  • 深入理解Cookie与Session会话管理
    本文详细介绍了如何通过HTTP响应和请求处理浏览器的Cookie信息,以及如何创建、设置和管理Cookie。同时探讨了会话跟踪技术中的Session机制,解释其原理及应用场景。 ... [详细]
  • CentOS7源码编译安装MySQL5.6
    2019独角兽企业重金招聘Python工程师标准一、先在cmake官网下个最新的cmake源码包cmake官网:https:www.cmake.org如此时最新 ... [详细]
  • 本文介绍了如何使用JQuery实现省市二级联动和表单验证。首先,通过change事件监听用户选择的省份,并动态加载对应的城市列表。其次,详细讲解了使用Validation插件进行表单验证的方法,包括内置规则、自定义规则及实时验证功能。 ... [详细]
  • 使用 Azure Service Principal 和 Microsoft Graph API 获取 AAD 用户列表
    本文介绍了一段通用代码示例,该代码不仅能够操作 Azure Active Directory (AAD),还可以通过 Azure Service Principal 的授权访问和管理 Azure 订阅资源。Azure 的架构可以分为两个层级:AAD 和 Subscription。 ... [详细]
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社区 版权所有