女排世界杯_1966世界杯 - ezrjnk120.com

Java的类加载器有哪些

2025-11-09 09:34:55

题目详细答案

在 Java 中,类加载器(ClassLoader)是负责将类文件加载到 JVM 中的组件。Java 提供了几种标准的类加载器,每种类加载器都有特定的职责和加载范围。

启动类加载器(Bootstrap ClassLoader)

职责:加载 Java 核心类库,如java.lang.*、java.util.*等。

实现:由本地代码(通常是 C++)实现,不是java.lang.ClassLoader的子类。

加载路径:$JAVA_HOME/jre/lib/rt.jar或jrt:/modules(在模块化系统中)。

特点:是所有类加载器的顶层,没有父类加载器。

扩展类加载器(Extension ClassLoader)

职责:加载扩展库中的类。

实现:由sun.misc.Launcher$ExtClassLoader实现,是java.lang.ClassLoader的子类。

加载路径:$JAVA_HOME/jre/lib/ext目录或由java.ext.dirs系统属性指定的目录。

父类加载器:引导类加载器。

应用程序类加载器(Application ClassLoader)

职责:加载应用程序类路径(classpath)中的类。

实现:由sun.misc.Launcher$AppClassLoader实现,是java.lang.ClassLoader的子类。

加载路径:由java.class.path系统属性指定的目录和 JAR 文件。

父类加载器:扩展类加载器。

自定义类加载器(Custom ClassLoader)

职责:满足特定需求的类加载器,通常在应用程序中自定义实现。

实现:继承java.lang.ClassLoader并重写findClass方法。

加载路径:由开发者自行定义,可以是文件系统、网络、数据库等。

父类加载器:可以指定,也可以继承应用程序类加载器。

以下是一个简单的自定义类加载器示例:

import java.io.IOException;

import java.nio.file.Files;

import java.nio.file.Paths;

public class MyClassLoader extends ClassLoader {

private String classPath;

public MyClassLoader(String classPath) {

this.classPath = classPath;

}

@Override

protected Class findClass(String name) throws ClassNotFoundException {

try {

// 将类名转换为文件路径

String fileName = classPath + name.replace('.', '/') + ".class";

// 读取类文件的字节码

byte[] classBytes = Files.readAllBytes(Paths.get(fileName));

// 将字节码转换为 Class 对象

return defineClass(name, classBytes, 0, classBytes.length);

} catch (IOException e) {

throw new ClassNotFoundException(name, e);

}

}

}

Java 类加载器详解

类加载器(ClassLoader)是 JVM 的基石,负责在运行时将 Java 类的字节码(.class 文件)动态地加载到内存中,并转换为 java.lang.Class 对象。Java 的类加载器采用了一种分层的、具有优先级的模型,即双亲委派模型。

一、Java 内置的三大类加载器

这三者构成了 Java 默认的类加载器层级结构。

类加载器

实现

父加载器

职责与加载路径

特点

启动类加载器

(Bootstrap ClassLoader)

C/C++ 代码,是 JVM 自身的一部分

null

(顶级)

加载 Java 核心库。

路径:$JAVA_HOME/jre/lib/rt.jar

、resources.jar

、sun.boot.class.path

系统变量指定的路径等。

1. 最顶层,不是 ClassLoader

的子类,Java 代码中无法直接引用。

2. 加载 java.*

, javax.*

等核心包。

扩展类加载器

(Extension ClassLoader)

Java 实现

(sun.misc.Launcher$ExtClassLoader

)

Bootstrap

加载 Java 的扩展库。

路径:$JAVA_HOME/jre/lib/ext

目录,或由 java.ext.dirs

系统变量指定的路径。

1. 开发者可将通用 JAR 包放入 ext

目录进行扩展。

2. 是 ClassLoader

的子类。

应用程序类加载器

(Application ClassLoader)

Java 实现

(sun.misc.Launcher$AppClassLoader

)

Extension

加载用户程序的类路径 (ClassPath)。

路径:由 java.class.path

系统变量或 -cp

/-classpath

命令行参数指定。

1. 也称作系统类加载器 (System ClassLoader)。

2. 是程序中默认的类加载器。ClassLoader.getSystemClassLoader()

返回的就是它。

层级关系:

Application ClassLoader -> (父) Extension ClassLoader -> (父) Bootstrap ClassLoader (在 Java 中视为 null)

二、自定义类加载器 (Custom ClassLoader)

当内置的三大类加载器无法满足需求时,就需要自定义类加载器。

目的:

加载非 ClassPath 路径的类:从网络、数据库、加密文件等特定来源加载类。实现代码隔离与热部署:如 Tomcat 为每个 Web 应用创建一个独立的类加载器,用于加载其 /WEB-INF/classes 和 /WEB-INF/lib 下的类,从而实现应用隔离和类的重新加载(热部署)。防止类冲突:允许不同版本的同名类共存。实现类加密:先加载加密的字节码,然后在自定义加载器中解密。

实现方法:

推荐做法是重写 findClass(String name) 方法,而不是重写 loadClass() 方法。

loadClass() 方法包含了双亲委派的逻辑,重写它意味着要自己实现整个委派机制,容易出错。findClass() 是 ClassLoader 设计为让子类扩展的方法。默认实现是抛异常。我们只需重写此方法,告诉 JVM 如何从自定义位置找到并定义类,而双亲委派的逻辑由 loadClass() 的父类实现来保证。

代码示例与分析:

import java.io.IOException;

import java.nio.file.Files;

import java.nio.file.Paths;

public class MyClassLoader extends ClassLoader {

private String classPath; // 自定义的类文件存放路径

public MyClassLoader(String classPath) {

// 指定父加载器为系统类加载器,遵循双亲委派

// 也可以不指定,默认就是 getSystemClassLoader()

super(ClassLoader.getSystemClassLoader());

this.classPath = classPath;

}

@Override

protected Class findClass(String name) throws ClassNotFoundException {

try {

// 1. 根据类名,构造.class文件在磁盘上的完整路径

// 将包名中的 '.' 替换为文件系统的分隔符 '/'

String fileName = classPath + name.replace('.', '/') + ".class";

// 2. 读取该文件的字节码

byte[] classBytes = Files.readAllBytes(Paths.get(fileName));

// 3. (可选) 在这里可以对 classBytes 进行解密操作

// classBytes = decrypt(classBytes);

// 4. 调用 defineClass,将字节数组转换为 java.lang.Class 对象

// defineClass 方法会完成JVM内部的验证、解析等过程

return defineClass(name, classBytes, 0, classBytes.length);

} catch (IOException e) {

throw new ClassNotFoundException("Could not load class: " + name, e);

}

}

// 可选:解密方法

// private byte[] decrypt(byte[] encryptedBytes) { ... }

}

使用自定义加载器:

public class Test {

public static void main(String[] args) throws Exception {

MyClassLoader myLoader = new MyClassLoader("/path/to/your/classes/");

// 使用自定义加载器加载指定类

Class myClass = myLoader.loadClass("com.yourcompany.YourClass");

// 创建实例并调用方法

Object instance = myClass.newInstance();

// ...

// 查看这个类是由哪个加载器加载的

System.out.println("Loaded by: " + myClass.getClassLoader());

System.out.println("Parent of myLoader: " + myLoader.getParent());

}

}

三、关键总结

双亲委派模型:是理解类加载器的核心。它保证了 Java 核心库的安全性和类的唯一性。层级结构:Bootstrap -> Extension -> Application -> Custom。加载请求逐级向上委派。命名空间:每个类由其全限定名和加载它的类加载器共同唯一确定。这意味着即使来自同一个 .class 文件,被两个不同的类加载器加载后,它们也是两个完全不同的类型,会导致 ClassCastException。可见性:子类加载器可以“看见”父类加载器加载的类,反之则不行。自定义加载器:通过继承 ClassLoader 并重写 findClass() 方法来实现,是扩展 Java 类加载能力的标准方式。它允许从任何来源加载类,是实现模块化、热部署等技术的基础。