Java的ClassLoader加载机制
一、ClassLoader的概念
Java程序在运行的时候,JVM通过类加载机制(ClassLoader)把class文件加载到内存中,只有class文件被载入内存,才能被其他class引用,使程序正确运行起来.
二、ClassLoader的分类
Java中的ClassLoader有三种.
1. Bootstrap ClassLoader
由C++写的,由JVM启动.
启动类加载器,负责加载java基础类,对应的文件是%JRE_HOME/lib/ 目录下的rt.jar、resources.jar、charsets.jar和class等
2.Extension ClassLoader
Java类,继承自URLClassLoader
扩展类加载器,对应的文件是 %JRE_HOME/lib/ext 目录下的jar和class等
3.App ClassLoader
Java类,继承自URLClassLoader
系统类加载器,对应的文件是应用程序classpath目录下的所有jar和class等
三、ClassLoader的过程
1.三者关系
Java的类加载使用双亲委托机制来搜索类.三种ClassLoader存在父子关系,App ClassLoader的父类加载器是Extension ClassLoader,Extension ClassLoader的父类加载器是Bootstrap ClassLoader,要注意的一点是,这里的父子并不是继承关系.
我新建一个类Test来验证三者的关系.
- public class Test {
- public static void main(String[] args) {
- ClassLoader ClassLoader1 = Test.class.getClassLoader();
- ClassLoader ClassLoader2 = ClassLoader1.getParent();
- ClassLoader ClassLoader3 = ClassLoader2.getParent();
- System.out.println(ClassLoader1);
- System.out.println(ClassLoader2);
- System.out.println(ClassLoader3);
- }
- }
- sun.misc.Launcher$AppClassLoader@1bbf1ca
- sun.misc.Launcher$ExtClassLoader@1ff0dde
- null
当这三者中的某个ClassLoader要加载一个类时,会先委托它的父类加载器尝试加载,一直往上,如果最顶级的父类加载器没有找到该类,那么委托者则亲自到特定的地方加载,如果没找到,那么就抛出异常ClassNotFoundException.这里画张图来表示下
这里要注意一点:只有被同一个类加载器实例加载并且文件名相同的class文件才被认为是同一个class.
四、自定义ClassLoader
1.为什么要自定义ClassLoader
因为系统的ClassLoader只会加载指定目录下的class文件,如果你想加载自己的class文件,那么就可以自定义一个ClassLoader.
2.如何自定义ClassLoader
2.1
新建一个类继承自java.lang.ClassLoader,重写它的findClass方法。
2.2
将class字节码数组转换为Class类的实例
2.3
调用loadClass方法即可
3.例子
我先建一个叫Log的类,很简单,只有一句打印
- public class Log {
- public static void main(String[] args) {
- System.out.println("加载成功");
- }
- }
我把这个java文件放到D盘根目录,然后打开cmd,用javac命令把java文件转化为class文件
然后我新建一个MyClassLoader继承自ClassLoader
- public class MyClassLoader extends ClassLoader {
- @Override
- protected Class<?> findClass(String name) throws ClassNotFoundException {
- Class log = null;
- // 获取该class文件字节码数组
- byte[] classData = getData();
- if (classData != null) {
- // 将class的字节码数组转换成Class类的实例
- log = defineClass(name, classData, 0, classData.length);
- }
- return log;
- }
- private byte[] getData() {
- //指定路径
- String path = "D:/Log.class";
- File file = new File(path);
- FileInputStream in = null;
- ByteArrayOutputStream out = null;
- try {
- in = new FileInputStream(file);
- out = new ByteArrayOutputStream();
- byte[] buffer = new byte[1024];
- int size = 0;
- while ((size = in.read(buffer)) != -1) {
- out.write(buffer, 0, size);
- }
- } catch (IOException e) {
- e.printStackTrace();
- } finally {
- try {
- in.close();
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- return out.toByteArray();
- }
- }
最后测试一下,输出加载这个Log的class文件的加载器,并且利用反射调用它的mian方法.
- public class Test {
- public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, SecurityException, IllegalArgumentException, InvocationTargetException {
- MyClassLoader myClassLoader = new MyClassLoader();
- //查找Log这个class文件
- myClassLoader.findClass("Log");
- //加载Log这个class文件
- Class<?> Log = myClassLoader.loadClass("Log");
- System.out.println("类加载器是:"+Log.getClassLoader());
- //利用反射获取main方法
- Method method=Log.getDeclaredMethod("main", String[].class) ;
- Object object=Log.newInstance();
- String [] arg={"ad"};
- method.invoke(object, (Object)arg);
- }
- }
看下打印(有乱码问题,不过可以看出成功了)
- 类加载器是:MyClassLoader@29e357
- 鍔犺浇鎴愬姛
结束语:关于java的classLoader机制我就介绍这么多,后面我打算写下安卓的classLoader机制及热修复