Java类装载机制是Java语言中实现类隔离、多版本共存、动态扩展等特性的基础。Java类装载机制主要包括以下三个步骤:
- 加载:在Java虚拟机中查找、读入并生成类的二进制数据;
- 链接:将类的二进制数据合并到Java虚拟机中,同时对二进制数据进行校验、准备(为静态变量分配空间并设置默认初始值)、解析;
- 初始化:执行类的初始化,包括初始化静态变量和执行静态代码块。
下面分别讲解一下这三个步骤的详细内容。
加载
类加载器负责在运行时查找、生成类的二进制数据,并生成Java虚拟机中的Class对象。Java虚拟机定义了双亲委派模型,即类的加载会通过一系列的父类加载器完成,如果父类加载器无法完成类的加载工作,才由子类加载器自行完成。这样可以使得运行时Java虚拟机中的Java类形成类的层次结构,避免同名类的冲突。
链接
类的链接过程是Java虚拟机将类的二进制数据合并到Java虚拟机中,并对类的二进制数据进行检查(如是否符合Java语法规范)、准备(为静态变量分配空间并设置默认初始值)和解析(把类中的符号引用转换为直接引用)。在类未被初始化前,类的变量被分配到“方法区”中。
初始化
类的初始化包括执行类构造器方法clinit()和执行类初始化属性。类构造器方法clinit()是编译器自动生成的,方法中包括静态块和赋值操作(常量除外),其执行顺序由字节码中顺序决定。
下面是两个示例:
示例一:自定义类加载器
public class MyClassLoader extends ClassLoader {
@Override
public Class<?> loadClass(String name) throws ClassNotFoundException {
Class<?> c = findClass(name);
if (c == null) {
return super.loadClass(name);
}
return c;
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
byte[] b = loadClassData(name);
if (b == null) {
throw new ClassNotFoundException();
}
return defineClass(name, b, 0, b.length);
}
private byte[] loadClassData(String name) {
// 从本地磁盘或网络上读取类的字节码
}
}
自定义类加载器可以实现指定类的加载、加密字节码等功能,可以有效保证类的安全性。
示例二:运行时动态扩展
//初始化代码
List<String> classNameList = new ArrayList<>();
classNameList.add("com.example.MyClass");
//...后续代码
for (String className : classNameList) {
Class<?> clazz = Class.forName(className);
// do something with clazz
}
运行时动态扩展可以实现在程序运行期间动态添加类,常见于插件式架构中。
总结:Java类装载机制是Java语言的核心机制之一,涉及到虚拟机、类加载器、类的运行时生成等多个方面。正确地了解和使用类的装载机制,可以帮助我们更好地编写出高质量、易于维护、可扩展性好的Java程序。