类加载实例

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

posted @ 2025-03-10 17:48  lllrrrqqq  阅读(12)  评论(0)    收藏  举报