要自定义自己的类加载器来加载类,需要先对类加载器和类加载机制有一些基本的了解。
1、类加载器
类加载器ClassLoader的作用有两个:
①是用于将class文件加载到JVM。
②是用于判断JVM运行时两个类是否相等。
2、类加载的时机
类的加载可分为隐式加载和显示加载。
隐式加载
隐式加载包括以下几种情况:
- 遇到new(new 一个实例对象的时候)、getstatic(获取一个类的静态字段的时候)、putstatic(设置一个类的静态字段的时候)、invokestatic(调用一个类的静态方法的时候)这4条字节码指令时。
- 对类进行反射调用时。
- 初始化一个类时,如果父类还没有初始化,则先加载其父类并初始化(但是初始化接口时,不要求先初始化父接口)
- 虚拟机启动时,需要指定一个包含main函数的主类,优先加载并初始化这个主类。
显式加载
显示加载包含以下几种情况:
- 通过Class.forName()加载
- 通过ClassLoader的loaderClass方法加载
- 通过ClassLoader的findClass方法
3、Class.forName()加载类
Class.forName()和ClassLoader都可以对类进行加载。ClassLoader就是遵循双亲委派模型最终调用启动类加载器的类加载器,实现的功能是“通过一个类的全限定名来获取描述此类的二进制字节流”,获取到二进制流后放到JVM中。Class.forName()方法实际上也是调用的CLassLoader来实现的。
先看看Class.forName()的源码:
/**
* 参数解释:
* 1、className:要加载的类名
* 2、true:class被加载后是否要被初始化。初始化即执行static的代码(静态代码)
* 3、caller:指定类加载器
*/
public static Class<?> forName(String className) throws ClassNotFoundException {
Class<?> caller = Reflection.getCallerClass();
return forName0(className, true, ClassLoader.getClassLoader(caller), caller);
}
可以看出来最后正在实现forName()方法的是forName0这一个native方法。而使用forName0需要传递的参数之一,就是ClassLoader类加载器。因此可以知道Class.forName()本质还是使用classloader来进行类的加载的。
4、使用ClassLoader加载类
ClassLoader 里面有三个重要的方法 loadClass()、findClass() 和 defineClass()。
loadClass() 方法是加载目标类的入口,它首先会查找当前ClassLoader以及它的父类classloader里面是否已经加载了目标类,如果没有找到就会让父类Classloader尝试加载,如果父类classloader都加载不了,就会调用findClass()让自定义加载器自己来加载目标类。这实际上就是双亲委派机制的原理。
看一下loadClass()的源码:
protected Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException
{
synchronized (getClassLoadingLock(name)) {
// First, check if the class has already been loaded
//查看类是否已被加载,findLoadedClass最后调用native方法findLoadedClass0
Class<?> c =