类加载器与双亲委派模型
前言:类加载流程
关于类加载流程,详细内容可以参考我的上一篇文章。此处简要总结下。
类加载分为加载-链接-初始化三个阶段:
加载:将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)试图加载某个类型的时候, 除非父加载器找不到相应类型,否则尽量将这个任务代理给当前加载器的父加载器去做。
双亲委派模型的好处:
安全性,避免用户自己编写的类动态替换 Java的一些核心类,比如 java.lang.String。
唯一性 避免类的重复加载。因为 JVM中区分不同类,不仅仅是根据类名,相同的 class文件被不同的 ClassLoader加载就是不同的两个类。
自定义类加载器
注意的地方:
Obj.class文件不能放在classpath路径下。因为双亲委派模型父类会先加载,如果在父类类加载器路径下,则会由父类进行加载。
重写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?