类加载器与双亲委派模型

前言:类加载流程

关于类加载流程,详细内容可以参考我的上一篇文章。此处简要总结下。

类加载分为加载-链接-初始化三个阶段:

  • 加载:将class文件二进制字节码数据读入jvm的方法区,在堆中生成对应class对象。

  • 链接:验证字节信息符合jvm规范,初始化静态变量初始值,将常量池中符号引用替换为直接引用。

  • 初始化:执行类初始化逻辑,即clinit方法,包括静态变量赋值动作,静态代码块。

本文讨论的类加载器ClassLoader,在加载阶段将 class 的字节码转换成class对象。

类加载器

  • 启动类加载器(Bootstrap Class-Loader),加载 jre/lib 下面的 jar 文件,如 rt.jar。常用内置库 java.xxx.* 都在里面,如 java.util.*、java.io.*、java.nio.*、java.lang.* 等

  • 扩展类加载器(Extension or Ext Class-Loader),负责加载我们放到 jre/lib/ext/ 目录下 面的 jar 包

  • 应用类加载器(Application or App Class-Loader),加载 Classpath 环境变量里定义的路径中的 jar 包和目录,也是系统类加载器。

双亲委派模型:

定义:简单说就是当类加载器(Class-Loader)试图加载某个类型的时候, 除非父加载器找不到相应类型,否则尽量将这个任务代理给当前加载器的父加载器去做。

双亲委派模型的好处:

  1. 安全性,避免用户自己编写的类动态替换 Java的一些核心类,比如 java.lang.String。

  2. 唯一性 避免类的重复加载。因为 JVM中区分不同类,不仅仅是根据类名,相同的 class文件被不同的 ClassLoader加载就是不同的两个类。

自定义类加载器

注意的地方:

  1. Obj.class文件不能放在classpath路径下。因为双亲委派模型父类会先加载,如果在父类类加载器路径下,则会由父类进行加载。

  2. 重写ClassLoader类中findClass()方法。通过类全限定名找到字节码文件转为字节数组,在将数组转换为class对象。

public class MyClassLoader extends ClassLoader {

    private String classPath;

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

    /**
     * 自定义类加载器,重写findClass()方法
     * 通过类全名找到class文件,转换成字节数组,转化成class对象
     */
    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        try {
            //通过全限定名找到class文件转成二进制字节数组
            byte[] data = loadByte(name);
            // 将字节数组转成class对象
            return defineClass(name, data, 0, data.length);
        } catch (Exception e) {
            e.printStackTrace();
            throw new ClassNotFoundException();
        }
    }

    /**
     * 将class文件转换成字节数组
     */
    private byte[] loadByte(String name) throws Exception {
        name = name.replaceAll("\\.", "/");
        try (FileInputStream fis = new FileInputStream(classPath + "/" + name + ".class")) {
            int len = fis.available();
            byte[] data = new byte[len];
            fis.read(data);
            return data;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }


    public static void main(String args[]) throws Exception {
        MyClassLoader myClassLoader = new MyClassLoader("");
        Class clazz = myClassLoader.loadClass("Obj");
        System.out.println(clazz.getClassLoader());

//        Object obj = clazz.newInstance();
//        Method helloMethod = clazz.getDeclaredMethod("hello", null);
//        helloMethod.invoke(obj, null);
    }

}

Last updated

Was this helpful?