文章目录
1. ClassLoader与现有类加载器的关系
ClassLoader 是一个抽象类。如果我们给定了一个类的二进制名称,类加载器应尝试去定位或生成构成定义类的数据。一种典型的策略是将给定的二进制名称转换为文件名,然后去文件系统中读取这个文件名所对应的 class文件。
2. ClassLoader 的主要方法
抽象类 ClassLoader 的主要方法如下:(内部没有抽象方法)
public final ClassLoader getParent()
返回该类加载器的超类加载器
public Class<?> loadClass(String name) throws ClassNotFoundException
加载名称为name的类,返回结果为java.lang.Class类的实例。如果找不到类,则返回 ClassNotFoundException 异常。该方法中的逻辑就是双亲委派模式的实现。
protected Class<?> findClass(String name) throws ClassNotFoundException
查找二进制名称为name的类,返回结果为java.lang.Class类的实例。这是一个受保护的方法,JVM鼓励我们重写此方法,需要自定义加载器遵循双亲委托机制,该方法会在检查完父类加载器之后被loadClass()方法调用。
protected final Class<?> defineClass(String name, byte[] b, int off, int len);
根据给定的字节数组b转换为Class的实例,off 和 len 参数表示实际 Class 信息在 byte 数组中的位置和长度,其中 byte 数组b是 ClassLoader 从外部获取的。这是受保护的方法,只有在自定义 ClassLoader 子类中可以使用。
简单举例:
protected final void resolveClass(Class<?> c)
链接指定的一个 Java 类。使用该方法可以使用类的 Class 对象创建完成的同时也被解析。前面我们说链接阶段主要是对字节码进行验证,为类变量分配内存并设置初始值同时将字节码文件中的符号引用转换为直接引用。
protected final Class<?> findLoadedClass(String name)
查找名称为name的已经被加载过的类,返回结果为java.lang.Class
类的实例。这个方法是final
方法,无法被修改。
private final ClassLoader parent;
它也是一个 ClassLoader
的实例,这个字段所表示的ClassLoader
也称为这个 ClassLoader
的双亲。在类加载的过程中, ClassLoader
可能会将某些请求交予自己的双亲处理。
3. SecureClassLoader 与 URLClassLoader
接着 SecureClassLoader
扩展了 ClassLoader,新增了几个与使用相关的代码源(对代码源的位置及其证书的验证)和权限定义类验证(主要指对class源码的访问权限)的方法,一般我们不会直接跟这个类打交道,更多是与它的子类URLClassLoader有所关联。
前面说过,ClassLoader是一个抽象类,很多方法是空的没有实现,比如 findClass()
、findResource()
等。而 URLClassLoader
这个实现类为这些方法提供了具体的实现。并新增了URLClassPath类协助取得Class字节码流等功能。在编写自定义类加载器时,如果没有太过于复杂的需求,可以直接继承URLClassLoader类,这样就可以避免自己去编写findClass()方法及其获取字节码流的方式,使自定义类加载器编写更加简洁。
4. ExtClassLoader 与 AppClassLoader
了解完URLClassLoader后接着看看剩余的两个类加载器,即拓展类加载器ExtClassLoader和系统类加载器AppClassLoader,这两个类都继承自URLClassLoader,是sun.misc.Launcher的静态内部类。sun.misc.Launcher主要被系统用于启动主应用程序,ExtClassLoader和AppClassLoader都是由sun.misc.Launcher创建的,其类主要类结构如下:
我们发现ExtClassLoader并没有重写loadClass()方法,这足矣说明其遵循双亲委派模式,而AppClassLoader重载了loadClass()方法,但最终调用的还是父类loadClass()方法,因此依然遵守双亲委派模式。
5. Class.forName() 与 ClassLoader.loadClass()
Class.forName():是一个静态方法,最常用的是
Class.forName(String className);
根据传入的类的全限定名返回一个 Class 对象。该方法在将 Class
文件加载到内存的同时,会执行类的初始化。如: Class.forName("com.atguigu.java.HelloWorld");
ClassLoader.loadClass():这是一个实例方法,需要一个 ClassLoader 对象来调用该方法。该方法将 Class 文件加载到内存时,并不会执行类的初始化,直到这个类第一次使用时才进行初始化。该方法因为需要得到一个 ClassLoader 对象,所以可以根据需要指定使用哪个类加载器。
如:
ClassLoader cl=.......;
cl.loadClass("com.XXX.java.HelloWorld");