0x01 类加载器:
类的生命周期:加载(类加载器负责)------->连接(验证类是否合法,分配类静态变量内存空间并赋值,解析地址)---->初始化--->使用(调用静态方法,new实例)------>卸载
类加载器的作用:加载java文件到JVM
常见的类加载器:
(1)Bootstrap ClassLoader(引导类加载器)---顶层类加载器--由c++实现
(2)Extension ClassLoader(扩展类加载器)---加载jre
(3)App ClassLoader(系统类加载器)---代码默认的类加载器
(4)CustomClassLoader----自定义的类加载器------重写findClass等,继承ClassLoader
引用简书大佬一张图:
注:class forname实际也是调用类加载器加载class
一段代码领略他们之间的关系
debug 至getClassLoader方法具体实现,78行调用getClassLoader0
getClassLoader0返回了classLoader
运行结果
重点:
ClassLoader类:
其主要的几个方法:
loadClass
(加载指定的Java类)findClass
(查找指定的Java类)findLoadedClass
(查找JVM已经加载过的类)defineClass
(定义一个Java类)resolveClass
(链接指定的Java类)
Class.forname:
静态看一下forname的实现
Class<?> caller = Reflection.getCallerClass();//返回当前调用类
forName0(className, true, ClassLoader.getClassLoader(caller), caller);//返回当前类的类加载器,forName0为native方法,ClassLoader.getClassLoader具体实现
跟进getClassLoader0, 发现具体方法与上面的一个demo一样,都是返回当前类加载器
Class.forName与类加载器的区别与联系:
// 反射加载Memory示例
Class.forName("com.test.Memory");
// ClassLoader加载Memory示例
this.getClass().getClassLoader().loadClass("com.test.Memory");
区别:Class.forName("类名")默认会初始化被加载类的静态属性和方法,如果不希望初始化类可以使用Class.forName("类名", 是否初始化类, 类加载器),而ClassLoader.loadClass默认不会初始化类方法
联系:Class.forName本质也是通过调用类加载器实现
Thread.currentThread().getContextClassLoader():
获取当前线程的ClassLoader
运行结果
sun.misc.Launcher$AppClassLoader@18b4aac2
具体方法
其中contextClassLoader为当前填充的ClassLoader
0x02 反射
运行状态中(不是编译时),对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java的语言的反射机制
个人理解,反射用法,class forname调用类加载器加载类,newInstance实例化对象,getMethod获取方法,invoke调用方法
在开发漏洞验证框架为了可以将类路径参数化调用java类,比较方便,但是比较消耗内存,使用记录
demo:
运行结果:
Class.forName默认初始话静态变量静态方法
debug:
强制步入 :发现也是调用了当前了类的类加载器
0x03 双亲委派
某个特定的类加载器在接到加载类的请求时,首先将加载任务委托给父类加载器,依次追溯,直到最高的爷爷辈的。如果父类加载器可以完成类加载任务,就成功返回;只有父类加载器无法完成此加载任务时,才自己去加载;
双亲委托机制的最大的好处就是可以确保是安全的。这种机制保证了不会出现用户自己能定义java.lang.Object类的情况出现。类加载器除了用于加载类,也是安全的最基本屏障
双亲委派关键代码:
代码实现:ClassLoader的loadClass()方法中,先检查是否已经被加载过,若没有加载则调用父类加载器的loadClass()方法,若父加载器为空则默认使用启动类加载器作为父加载器。如果父加载失败,则抛出ClassNotFoundException异常后,再调用自己的findClass()方法进行加载。
demo:
如果自己实现了java.lang.Class,跑起来会跑错,防止自己定义java.*等一系列代码
参考文章:
https://www.jianshu.com/p/8df45255c4bd
https://javasec.org/javase/ClassLoader/
https://blog.csdn.net/qq_36582604/article/details/81100501
https://www.cnblogs.com/cl-rr/p/9081817.html
https://www.jianshu.com/p/554c138ca0f5
https://www.cnblogs.com/jimoer/p/9185662.html