一、项目概述
本实例通过自定义类加载器、演示类及示例类,验证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;}@Overrideprotected 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\\") {@Overridepublic 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类加载核心概念。