类加载实例
一、项目概述
本实例通过自定义类加载器、演示类及示例类,验证Java类加载机制中的双亲委派模型、破坏双亲委派场景,以及类加载器命名空间隔离特性。通过添加-verbose:class参数观察类加载过程,直观理解JVM类加载核心概念。
二、代码结构说明
1.自定义类加载器:CustomClassLoader
• 功能:从指定路径加载.class文件。
• 核心实现:
• 重写findClass方法,通过defineClass定义类。
• loadClassData方法读取类文件字节码,拼接类路径并读取文件内容。
package com.example.jvm.loader;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
public class CustomClassLoader extends ClassLoader {
private final String classPath;
public CustomClassLoader(String classPath) {
this.classPath = classPath;
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
System.out.println("【自定义加载器】开始加载类: " + name);
byte[] data = loadClassData(name);
return defineClass(name, data, 0, data.length);
}
private byte[] loadClassData(String className) throws Exception {
String path = classPath + className.replace('.', File.separatorChar) + ".class";
try (FileInputStream fis = new FileInputStream(path);
ByteArrayOutputStream bos = new ByteArrayOutputStream()) {
byte[] buffer = new byte[1024];
int len;
while ((len = fis.read(buffer)) != -1) {
bos.write(buffer, 0, len);
}
return bos.toByteArray();
}
}
}
2.演示类:ClassLoadingDemo
包含三个场景,演示类加载核心特性:
场景1:正常双亲委派流程
• 逻辑:使用自定义加载器加载类,触发默认双亲委派机制。
• 代码:
System.out.println("\n==== 场景1:正常类加载流程 ====");
CustomClassLoader loader1 = new CustomClassLoader("d:\\project\\Jvm\\out\\production\\classes\\");
Class<?> clazz1 = loader1.loadClass("com.example.jvm.demo.SampleClass");
System.out.println("类加载器: " + clazz1.getClassLoader());
场景2:破坏双亲委派机制
• 逻辑:重写loadClass方法,绕过双亲委派,直接由自定义加载器加载指定类。
• 代码:
System.out.println("\n==== 场景2:破坏双亲委派 ====");
CustomClassLoader loader2 = new CustomClassLoader("d:\\project\\Jvm\\out\\production\\classes\\") {
@Override
public Class<?> loadClass(String name) throws ClassNotFoundException {
if (name.startsWith("com.example.jvm")) {
return findClass(name); // 直接加载,破坏双亲委派
}
return super.loadClass(name);
}
};
Class<?> clazz2 = loader2.loadClass("com.example.jvm.demo.SampleClass");
System.out.println("破坏委派后加载器: " + clazz2.getClassLoader());
场景3:命名空间隔离
• 逻辑:使用不同自定义加载器实例加载同类,验证JVM将其视为不同类。
• 代码:
System.out.println("\n==== 场景3:命名空间隔离 ====");
CustomClassLoader loader3 = new CustomClassLoader("d:\\project\\Jvm\\out\\production\\classes\\");
CustomClassLoader loader4 = new CustomClassLoader("d:\\project\\Jvm\\out\\production\\classes\\");
Class<?> clazz3 = loader3.loadClass("com.example.jvm.demo.SampleClass");
Class<?> clazz4 = loader4.loadClass("com.example.jvm.demo.SampleClass");
System.out.println("相同加载器实例: " + (clazz3 == clazz4)); // false,不同加载器实例加载的类不同
System.out.println("不同加载器实例: " + (loader3 == loader4)); // false
3.示例类:SampleClass
• 功能:作为被加载的类,包含静态初始化块,验证类加载时的初始化行为。
• 代码:
package com.example.jvm.demo;
public class SampleClass {
static {
System.out.println("【类初始化】SampleClass被加载");
}
public void hello() {
System.out.println("Hello from SampleClass!");
}
}
三、运行现象总结
• 默认加载:未破坏双亲委派时,类由AppClassLoader(应用类加载器)加载。
• 破坏委派:重写loadClass后,类直接由自定义加载器加载。
• 命名空间隔离:不同自定义加载器实例加载的同类,在JVM中视为不同类(clazz3 == clazz4结果为false)。
通过本实例,直观验证了双亲委派机制、类加载器命名空间隔离等JVM类加载核心概念。
浙公网安备 33010602011771号