JAVA基础第一弹-类加载机制

117 阅读3分钟

大家好,今天和大家一起分享一下JAVA的类加载机制~

 

Java 类加载机制是 Java 虚拟机(JVM)中一个重要的组成部分,它负责将字节码文件加载到内存中,并对其进行验证、准备、解析和初始化,最终形成可以被虚拟机直接使用的 Java 类型。理解类加载机制对于深入掌握 Java 技术栈至关重要。

类加载器体系结构

Java 类加载器采用了一种称为“双亲委派模型”的层级结构。这种模型要求除了顶层的启动类加载器外,其余每个类加载器在尝试加载类时,都会先委托给其父类加载器进行加载,只有当父类加载器无法完成加载请求时,子类加载器才会尝试自己去加载。

  1. 启动类加载器(Bootstrap ClassLoader) :这是由C++实现的一个特殊类加载器,负责加载存放在 <JAVA_HOME>/lib 目录或被 -Xbootclasspath 参数指定路径中的、且是构成JRE基本运行环境所需的类库。
  2. 扩展类加载器(Extension ClassLoader) :这个加载器是由 sun.misc.Launcher$ExtClassLoader 实现的,它负责加载 <JAVA_HOME>/lib/ext 目录中的,或者由系统属性 java.ext.dirs 指定路径中的所有类库。
  3. 应用程序类加载器(Application ClassLoader) :这个加载器是由 sun.misc.Launcher$AppClassLoader 实现的,它负责加载用户类路径(ClassPath)上所指定的类库,开发者可以直接使用这个类加载器。
  4. 自定义类加载器:开发者可以根据需求编写自己的类加载器,继承自 java.lang.ClassLoader 类。

类加载过程

类加载过程主要分为五个阶段:加载、验证、准备、解析和初始化。

  • 加载:通过一个类的全限定名来获取该类的二进制字节流,然后将这个字节流转换成方法区内的运行时数据结构,在内存中生成一个代表这个类的 java.lang.Class 对象。
  • 验证:确保Class文件的字节流中包含的信息符合当前虚拟机的要求,并且不会危害虚拟机自身的安全。
  • 准备:为类的静态变量分配内存并设置默认初始值,这些内存都将在方法区中分配。
  • 解析:将常量池内的符号引用替换为直接引用。
  • 初始化:执行类构造器 () 方法的过程。类构造器 () 方法是由编译器自动收集类中的所有静态变量的赋值动作和静态代码块中的语句合并产生的,编译器收集的顺序是由语句在源文件中出现的顺序决定的。

示例代码

下面是一个简单的自定义类加载器的示例,该加载器从指定目录读取 .class 文件:

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;


public class MyClassLoader extends ClassLoader {

    private String classPath;

    public MyClassLoader(String classPath) {
        this.classPath = classPath;
    }

    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        byte[] data = loadClassData(name);
        return defineClass(name, data, 0, data.length);
    }

    private byte[] loadClassData(String className) {

        String path = classPath + File.separatorChar + className.replace('.', File.separatorChar) + ".class";

        try (FileInputStream fis = new FileInputStream(path);
             ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
            int bufferSize = 1024;
            byte[] buffer = new byte[bufferSize];
            int len;
            while ((len = fis.read(buffer)) != -1) {
                baos.write(buffer, 0, len);
            }
            return baos.toByteArray();
        } catch (IOException e) {
            e.printStackTrace();
            return null;
        }
    }

    public static void main(String[] args) {
        MyClassLoader myClassLoader = new MyClassLoader("path/to/classes");
        try {
            Class<?> clazz = myClassLoader.loadClass("com.example.MyClass");
            System.out.println("Class " + clazz.getName() + " loaded.");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

在这个例子中创建了一个名为 MyClassLoader 的自定义类加载器,它从文件系统中特定的路径加载 .class 文件。findClass 方法首先调用 loadClassData 方法来读取类文件的数据,然后使用 defineClass 方法将这些原始字节转换成 Class 对象。