← 返回首页

Java类加载机制详解

📂 java ⏱ 3 min 463 words

Java类加载机制详解

类加载过程

JVM加载一个类需要经过五个阶段:加载 → 验证 → 准备 → 解析 → 初始化。

源代码(.java) → 编译(.class) → 类加载 → 验证 → 准备 → 解析 → 初始化

加载(Loading)

// 类加载器负责将.class文件加载到内存
// 1. 通过全限定类名获取类的二进制字节流
// 2. 将字节流代表的静态存储结构转换为方法区的运行时数据结构
// 3. 在堆中生成一个java.lang.Class对象作为方法区数据的访问入口

public class ClassLoadingDemo {
    public static void main(String[] args) throws ClassNotFoundException {
        // 使用指定类加载器加载类
        ClassLoader loader = ClassLoadingDemo.class.getClassLoader();
        Class<?> clazz = loader.loadClass("com.example.User");
        System.out.println(clazz.getName());
    }
}

验证(Verification)

确保Class文件的字节流符合JVM规范。

// 验证阶段包括:
// 1. 文件格式验证:是否以0xCAFEBABE开头
// 2. 元数据验证:是否有父类、是否继承了final类
// 3. 字节码验证:方法体是否合法
// 4. 符号引用验证:引用的类/方法/字段是否存在

准备(Preparation)

为类的静态变量分配内存并设置默认值。

public class PrepareDemo {
    // 准备阶段:value = 0(默认值)
    // 初始化阶段:value = 123
    public static int value = 123;

    // final修饰的常量在编译期就已确定,准备阶段直接赋值
    public static final int CONSTANT = 456;
}

解析(Resolution)

将常量池中的符号引用替换为直接引用。

public class ResolveDemo {
    public void method() {
        // 符号引用:方法名"method"
        // 直接引用:方法在内存中的地址
    }
}

初始化(Initialization)

执行类构造器<clinit>()方法。

public class InitDemo {
    // 静态变量按顺序执行
    public static int a = 10;
    public static int b = a + 1; // b = 11

    // 静态代码块
    static {
        System.out.println("类初始化");
    }

    // 构造器(实例初始化)
    {
        System.out.println("实例初始化");
    }

    public InitDemo() {
        System.out.println("构造方法");
    }
}

触发初始化的情况

// 1. new对象
User user = new User();

// 2. 访问静态字段(final常量除外)
int value = MyClass.VALUE;

// 3. 调用静态方法
MyClass.staticMethod();

// 4. 反射
Class.forName("com.example.MyClass");

// 5. 子类初始化会先初始化父类
// 6. 主类(包含main方法的类)

双亲委派模型

Bootstrap ClassLoader (启动类加载器)
    ↓
Extension ClassLoader (扩展类加载器)
    ↓
Application ClassLoader (应用类加载器)
    ↓
Custom ClassLoader (自定义类加载器)
// 双亲委派机制
public class BootstrapClassLoader {
    // 加载Java核心类库(rt.jar等)
    // 由C++实现,Java中无法直接获取
}
// Extension ClassLoader
public class ExtClassLoaderDemo {
    // 加载Java扩展类库(ext目录)
    public static void main(String[] args) {
        ClassLoader extLoader = ClassLoader.getSystemClassLoader().getParent();
        System.out.println("Extension ClassLoader: " + extLoader);
    }
}
// Application ClassLoader
public class AppClassLoaderDemo {
    // 加载classpath下的类
    public static void main(String[] args) {
        ClassLoader appLoader = ClassLoader.getSystemClassLoader();
        System.out.println("Application ClassLoader: " + appLoader);
        // sun.misc.Launcher$AppClassLoader
    }
}

自定义类加载器

public class CustomClassLoader extends ClassLoader {

    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        try {
            // 将类名转换为文件路径
            String path = name.replace('.', '/') + ".class";
            InputStream is = getResourceAsStream(path);

            if (is == null) {
                throw new ClassNotFoundException(name);
            }

            byte[] data = is.readAllBytes();
            return defineClass(name, data, 0, data.length);
        } catch (IOException e) {
            throw new ClassNotFoundException(name, e);
        }
    }

    public static void main(String[] args) throws Exception {
        CustomClassLoader loader = new CustomClassLoader();
        Class<?> clazz = loader.loadClass("com.example.User");
        System.out.println("类加载器: " + clazz.getClassLoader());
        System.out.println("是否同一个类: " + (clazz == com.example.User.class));
    }
}

打破双亲委派

SPI机制

// JDBC使用SPI打破双亲委派
// 核心接口在rt.jar(Bootstrap加载)
// 实现在classpath(Application加载)

// 线程上下文类加载器
Thread.currentThread().getContextClassLoader();

热部署

// 实现类的热替换
public class HotSwapClassLoader extends ClassLoader {
    private String className;

    public HotSwapClassLoader(String className) {
        this.className = className;
    }

    public Class<?> reload() throws ClassNotFoundException {
        // 每次都重新加载类
        return findClass(className);
    }
}

类加载器隔离

// 同一个类被不同类加载器加载,JVM认为是不同的类
public class IsolationDemo {
    public static void main(String[] args) throws Exception {
        ClassLoader loader1 = new CustomClassLoader();
        ClassLoader loader2 = new CustomClassLoader();

        Class<?> clazz1 = loader1.loadClass("com.example.User");
        Class<?> clazz2 = loader2.loadClass("com.example.User");

        System.out.println(clazz1 == clazz2); // false
    }
}

总结

类加载机制是JVM的重要组成部分。理解加载过程、双亲委派模型和自定义类加载器,对于实现热部署、模块化和类隔离等高级特性非常有帮助。