热门标签 | 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. 自定义类加载器的应用场景
自定义类加载器主要用于处理标准类加载器无法满足的需求,如加载网络上的类文件、动态生成的类、加密后的类文件等。通过自定义类加载器,可以灵活地控制类的加载行为,满足多样化的应用场景。
推荐阅读
  • Java 架构:深入理解 JDK 动态代理机制
    代理模式是 Java 中常用的设计模式之一,其核心在于代理类与委托类共享相同的接口。代理类主要用于为委托类提供预处理、过滤、转发及后处理等功能,以增强或改变原有功能的行为。 ... [详细]
  • 2017-2018年度《网络编程与安全》第五次实验报告
    本报告详细记录了2017-2018学年《网络编程与安全》课程第五次实验的具体内容、实验过程、遇到的问题及解决方案。 ... [详细]
  • 深入理解Java类加载机制
    本文详细探讨了Java虚拟机(JVM)中类加载器的工作原理,特别是如何通过类的全限定名从外部源获取二进制字节流,以及不同类型的类加载器及其在双亲委派模型中的角色。 ... [详细]
  • 本文将详细探讨 Java 中提供的不可变集合(如 `Collections.unmodifiableXXX`)和同步集合(如 `Collections.synchronizedXXX`)的实现原理及使用方法,帮助开发者更好地理解和应用这些工具。 ... [详细]
  • 本文探讨了如何在Java中使用JAXB解组两个具有相同名称但不同结构的对象。我们将介绍一个抽象类Bar及其具体实现,并展示如何正确地解析XML文档以获取正确的对象实例。 ... [详细]
  • 在使用 Spring Cloud Config 作为配置中心时,若在配置文件中指定了请求路径但未能生效,本文将探讨其原因及解决方案。 ... [详细]
  • 本文深入探讨了Java中的代理模式,包括静态代理和动态代理的概念、实现及其应用场景。通过具体的代码示例,详细解析了如何利用代理模式增强代码的功能性和灵活性。 ... [详细]
  • 随着毕业设计的结束,我终于有时间更新我的博客了。这次,我将分享如何在自己的服务器上搭建 Bitwarden,一个广受好评的开源密码管理工具。 ... [详细]
  • 本文探讨了如何利用HTML5和JavaScript在浏览器中进行本地文件的读取和写入操作,并介绍了获取本地文件路径的方法。HTML5提供了一系列API,使得这些操作变得更加简便和安全。 ... [详细]
  • Asp.net MVC 中 Bundle 配置详解:合并与压缩 JS 和 CSS 文件
    本文深入探讨了 Asp.net MVC 中如何利用 Bundle 功能来合并和压缩 JavaScript 和 CSS 文件,提供了详细的配置步骤和示例代码,适合开发人员参考学习。 ... [详细]
  • window下kafka的安装以及测试
    目录一、安装JDK(需要安装依赖javaJDK)二、安装Kafka三、测试参考在Windows系统上安装消息队列kafka一、安装JDKÿ ... [详细]
  • 本文探讨了使用Filter作为控制器的优势,以及Servlet与Filter之间的主要差异。同时,详细解析了Servlet的工作流程及其生命周期,以及ServletConfig与ServletContext的区别与应用场景。 ... [详细]
  • 本文介绍了如何在Ubuntu 16.04系统上配置Nginx服务器,以便能够通过网络访问存储在服务器上的图片资源。这解决了在网页开发中需要使用自定义在线图标的需求。 ... [详细]
  • 了解如何快速搭建属于自己的个人博客,无需编程基础,适合Mac和Windows用户。通过本文,您将学会使用GitHub Pages和Hexo构建一个完全自主的在线空间。 ... [详细]
  • 如何解决PHP中时间获取不准确的问题
    本文探讨了在PHP开发过程中遇到的时间获取错误问题,并提供了详细的解决方案,包括通过修改配置文件和编程方法来调整时区设置。 ... [详细]
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社区 版权所有