← 返回首页
📦

Java类加载机制详解:ClassLoader与双亲委派

📂 java ⏱ 4 min 604 words

Java类加载机制详解:ClassLoader与双亲委派

概述

Java类加载机制是JVM将类的.class文件加载到内存,并对数据进行校验、转换解析和初始化,最终形成可以被虚拟机直接使用的Java类型的过程。

1. 类加载过程

类的生命周期

加载 -> 验证 -> 准备 -> 解析 -> 初始化 -> 使用 -> 卸载
public class ClassLoadingProcess {
    // 加载:将.class文件读入内存
    // 验证:确保Class文件的字节流包含的信息符合JVM规范
    // 准备:为类的静态变量分配内存,并设置默认值
    // 解析:将符号引用替换为直接引用
    // 初始化:执行类构造器<clinit>()方法
    
    static int value = 10;  // 准备阶段:value = 0
                           // 初始化阶段:value = 10
    
    static {
        System.out.println("类初始化");
    }
    
    public static void main(String[] args) {
        System.out.println("main方法执行");
    }
}

2. ClassLoader层次

ClassLoader类型

public class ClassLoaderHierarchy {
    public static void main(String[] args) {
        // 获取类加载器层次
        ClassLoader appClassLoader = ClassLoaderHierarchy.class.getClassLoader();
        ClassLoader extClassLoader = appClassLoader.getParent();
        ClassLoader bootstrapClassLoader = extClassLoader.getParent();
        
        System.out.println("应用类加载器: " + appClassLoader);
        System.out.println("扩展类加载器: " + extClassLoader);
        System.out.println("启动类加载器: " + bootstrapClassLoader);
        
        // Bootstrap ClassLoader:加载rt.jar、charsets.jar等
        // Extension ClassLoader:加载ext目录下的jar包
        // Application ClassLoader:加载classpath下的类
        
        // 获取系统类加载器
        ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
        System.out.println("系统类加载器: " + systemClassLoader);
        
        // 获取当前线程上下文类加载器
        ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
        System.out.println("上下文类加载器: " + contextClassLoader);
    }
}

3. 双亲委派模型

public class ParentDelegationModel {
    // 双亲委派模型的工作流程:
    // 1. 类加载请求先委派给父类加载器
    // 2. 父类加载器继续向上委派,直到启动类加载器
    // 3. 如果父类加载器无法完成加载,才由子类加载器尝试加载
    
    // 优点:
    // - 避免类的重复加载
    // - 保证Java核心类的安全性
    
    // 源码分析(简化):
    // protected Class<?> loadClass(String name, boolean resolve)
    //     throws ClassNotFoundException {
    //     // 1. 检查类是否已加载
    //     Class<?> c = findLoadedClass(name);
    //     if (c == null) {
    //         try {
    //             // 2. 委派给父类加载器
    //             if (parent != null) {
    //                 c = parent.loadClass(name, false);
    //             } else {
    //                 c = findBootstrapClassOrNull(name);
    //             }
    //         } catch (ClassNotFoundException e) {
    //             // 父类加载器无法加载
    //         }
    //         if (c == null) {
    //             // 3. 自己尝试加载
    //             c = findClass(name);
    //         }
    //     }
    //     return c;
    // }
    
    public static void main(String[] args) {
        System.out.println("双亲委派模型示例");
    }
}

4. 自定义ClassLoader

import java.io.*;

public class CustomClassLoader extends ClassLoader {
    private String classPath;
    
    public CustomClassLoader(String classPath) {
        this.classPath = classPath;
    }
    
    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        try {
            String fileName = name.replace(".", File.separator) + ".class";
            String filePath = classPath + File.separator + fileName;
            
            FileInputStream fis = new FileInputStream(filePath);
            byte[] data = fis.readAllBytes();
            fis.close();
            
            return defineClass(name, data, 0, data.length);
        } catch (IOException e) {
            throw new ClassNotFoundException("无法加载类: " + name, e);
        }
    }
    
    public static void main(String[] args) {
        try {
            CustomClassLoader loader = new CustomClassLoader("target/classes");
            Class<?> clazz = loader.loadClass("com.example.MyClass");
            
            System.out.println("类加载器: " + clazz.getClassLoader());
            System.out.println("类名: " + clazz.getName());
            
        } catch (ClassNotFoundException e) {
            System.out.println("类未找到: " + e.getMessage());
        }
    }
}

5. 热部署

import java.io.*;
import java.lang.reflect.*;

public class HotDeployment {
    private CustomClassLoader classLoader;
    private Object instance;
    
    public HotDeployment(String classPath) {
        this.classLoader = new CustomClassLoader(classPath);
    }
    
    public void loadClass(String className) throws Exception {
        // 创建新的类加载器
        classLoader = new CustomClassLoader(classPath);
        
        // 加载类
        Class<?> clazz = classLoader.loadClass(className);
        
        // 创建实例
        instance = clazz.getDeclaredConstructor().newInstance();
        
        System.out.println("类已加载: " + className);
    }
    
    public void reload(String className) throws Exception {
        System.out.println("重新加载类: " + className);
        loadClass(className);
    }
    
    public static void main(String[] args) {
        try {
            HotDeployment deployment = new HotDeployment("target/classes");
            deployment.loadClass("com.example.MyClass");
            
            // 模拟热部署
            Thread.sleep(5000);
            deployment.reload("com.example.MyClass");
            
        } catch (Exception e) {
            System.out.println("热部署异常: " + e.getMessage());
        }
    }
}

6. 实际应用示例

插件系统

import java.io.*;
import java.net.*;
import java.util.*;

public class PluginLoader {
    private Map<String, Class<?>> plugins = new HashMap<>();
    private List<CustomClassLoader> classLoaders = new ArrayList<>();
    
    public void loadPlugin(String pluginPath, String mainClass) throws Exception {
        CustomClassLoader loader = new CustomClassLoader(pluginPath);
        Class<?> clazz = loader.loadClass(mainClass);
        
        plugins.put(mainClass, clazz);
        classLoaders.add(loader);
        
        System.out.println("插件已加载: " + mainClass);
    }
    
    public Object createPluginInstance(String mainClass) throws Exception {
        Class<?> clazz = plugins.get(mainClass);
        if (clazz == null) {
            throw new ClassNotFoundException("插件未找到: " + mainClass);
        }
        return clazz.getDeclaredConstructor().newInstance();
    }
    
    public void unloadAll() {
        plugins.clear();
        classLoaders.clear();
        System.out.println("所有插件已卸载");
    }
    
    public static void main(String[] args) {
        try {
            PluginLoader loader = new PluginLoader();
            
            // 加载插件
            loader.loadPlugin("plugins/plugin1", "com.example.Plugin1");
            loader.loadPlugin("plugins/plugin2", "com.example.Plugin2");
            
            // 使用插件
            Object plugin1 = loader.createPluginInstance("com.example.Plugin1");
            System.out.println("插件1实例: " + plugin1);
            
            // 卸载所有插件
            loader.unloadAll();
            
        } catch (Exception e) {
            System.out.println("插件加载异常: " + e.getMessage());
        }
    }
}

7. 最佳实践

  1. 理解双亲委派:了解类加载器的层次和委派机制
  2. 避免破坏双亲委派:除非有特殊需求,否则不要破坏双亲委派模型
  3. 注意内存泄漏:自定义ClassLoader可能导致内存泄漏
  4. 合理使用热部署:热部署适用于插件系统等场景
  5. 测试类加载:确保自定义ClassLoader正确加载类

总结

Java类加载机制是JVM的重要组成部分。理解类加载过程、ClassLoader层次和双亲委派模型,对于开发插件系统、热部署等高级功能非常重要。在实际编程中,要合理使用类加载机制,避免常见问题。