前言
在Java中,类加载器(ClassLoader)是Java虚拟机(JVM)的一个重要组成部分,负责将.class文件加载到内存中并生成对应的Class对象。类加载器的主要任务是动态加载Java类,即在程序运行时根据需要加载类,而不是在编译时就全部加载。
JVM类加载机制
JVM的类加载机制遵循一定的规则和顺序,主要包括以下几个步骤:
-
加载(Loading)
加载阶段是指查找并加载类的二进制数据(.class文件)到JVM中的内存中。加载类的任务由类加载器完成,JVM内置了三个主要的类加载器:
-
Bootstrap ClassLoader:也称为引导类加载器,负责加载JVM自身需要的类,如
java.lang.Object
等核心类库,它是JVM的一部分,通常用本地代码实现,无法直接在Java代码中获取到。 -
Extension ClassLoader:扩展类加载器,负责加载JVM扩展目录(
<JAVA_HOME>/lib/ext
目录)中的类库。在Java中可以通过ClassLoader.getSystemClassLoader().getParent()
获取到扩展类加载器。 -
System ClassLoader:也称为应用程序类加载器,负责加载应用程序classpath下的类,即开发者自己编写的类。在Java中可以通过
ClassLoader.getSystemClassLoader()
获取到应用程序类加载器。
-
-
链接(Linking)
链接阶段包括三个子阶段:
-
验证(Verification):确保加载的类符合JVM规范,如字节码验证,验证类文件是否有效。
-
准备(Preparation):为类的静态变量分配内存,并设置默认初始值(零值)。
-
解析(Resolution):将类中的符号引用替换为直接引用,即转化为可以直接使用的内存地址。
-
-
初始化(Initialization)
初始化阶段是类加载的最后一个阶段,负责执行类变量的初始化和静态代码块的执行。初始化类的条件包括:
- 创建类的实例。
- 访问类的静态变量或静态方法。
- 初始化类的子类。
类的初始化是按照初始化顺序执行静态变量和静态代码块,且只会执行一次。
-
使用(Using)
在使用阶段,类被实际使用,包括创建对象、调用方法等操作。
-
卸载(Unloading)
类卸载阶段是指JVM在满足一定条件时,卸载不再需要的类,释放相关的内存空间。类卸载的条件包括类的实例都已经被GC回收,并且该类的ClassLoader也没有被引用。
类加载器(ClassLoader)
类加载器是Java的一个重要概念,它负责加载Java类到内存中,并生成对应的Class对象。类加载器根据不同的加载需求和类路径,将类加载到JVM中,它的主要特点包括:
-
双亲委派模型:JVM采用双亲委派模型来组织类加载器。即当一个类加载器接收到加载类的请求时,它会先委托给父类加载器加载,只有在父类加载器无法完成加载时,子类加载器才会尝试加载。这样可以保证类的加载顺序和避免重复加载。
-
类加载器层次:JVM中的类加载器形成了层次结构,从而形成了加载类的优先级。Bootstrap ClassLoader位于顶层,Extension ClassLoader和System ClassLoader位于其下,开发者可以根据需要自定义ClassLoader来加载类。
-
自定义类加载器:开发者可以通过继承ClassLoader类来自定义类加载器,以实现特定的加载需求,如从网络中加载类、加密类加载等。
示例
以下是一个简单的自定义类加载器示例,用于从指定路径加载类:
public class CustomClassLoader extends ClassLoader {
private String classPath;
public CustomClassLoader(String classPath) {
this.classPath = classPath;
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
try {
byte[] data = loadClassData(name);
return defineClass(name, data, 0, data.length);
} catch (IOException e) {
throw new ClassNotFoundException(name);
}
}
private byte[] loadClassData(String className) throws IOException {
String fileName = classPath + File.separatorChar + className.replace('.', File.separatorChar) + ".class";
try (InputStream ins = new FileInputStream(fileName)) {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
int bufferSize = 1024;
byte[] buffer = new byte[bufferSize];
int bytesNumRead;
while ((bytesNumRead = ins.read(buffer)) != -1) {
baos.write(buffer, 0, bytesNumRead);
}
return baos.toByteArray();
}
}
public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
String classPath = "/path/to/classes";
CustomClassLoader loader = new CustomClassLoader(classPath);
Class<?> clazz = loader.loadClass("com.example.MyClass");
Object obj = clazz.newInstance();
// 使用加载的类实例
}
}
总结
类加载器和JVM类加载机制是Java语言动态性和灵活性的基础之一。了解类加载的过程、类加载器的种类和工作原理,对于深入理解Java的类加载机制和解决类加载问题至关重要。
⭐️⭐️ ⭐️ ⭐️ ⭐️ 好书推荐
《Java项目开发全程实录》(第4版)
【内容简介】
《Java项目开发全程实录(第4版)》以企业QQ、蓝宇快递打印系统、开发计划管理系统、酒店管理系统、图书馆管理系统、学生成绩管理系统、进销存管理系统、神奇Book—图书商城、企业门户网站、棋牌游戏系统之网络五子棋10个实际项目开发程序为案例,从软件工程的角度出发,按照项目的开发顺序,系统、全面地介绍了J2SE和J2EE项目的开发流程。从开发背景、需求分析、系统功能分析、数据库分析、数据库建模、网站开发和网站发布或者程序打包与运行方面进行讲解,每一过程都进行了详细的介绍。
📚 京东购买链接:Java项目开发全程实录(第4版)https://u.jd.com/5QShvEG