类加载器

所有 Java 应用中的类都是被 java.lang.ClassLoader 类的一系列子类加载的。因此要想动态加载类的话也必须使用 java.lang.ClassLoader 的子类。
一个类一旦被加载时,这个类引用的所有类也同时会被加载。类加载过程是一个递归的模式,所有相关的类都会被加载。但并不一定是一个应用里面所有类都会被加载,与这个被加载类的引用链无关的类是不会被加载的,直到有引用关系的时候它们才会被加载。

类加载体系

在 Java 中类加载是一个有序的体系。当你新创建一个标准的 Java 类加载器时你必须提供它的父加载器。当一个类加载器被调用来加载一个类的时候,首先会调用这个加载器的父加载器来加载。如果父加载器无法找到这个类,这时候这个加载器才会尝试去加载这个类。

类加载

类加载器加载类的顺序如下:

  1. 检查这个类是否已经被加载。
  2. 如果没有被加载,则首先调用父加载器加载。
  3. 如果父加载器不能加载这个类,则尝试加载这个类。当你实现一个有重载类功能的类加载器,它的顺序与上述会有些不同。

类重载不会请求的他的父加载器来进行加载。

动态类加载

动态加载一个类十分简单。你要做的就是获取一个类加载器然后调用它的 loadClass() 方法。

  1. public class MainClass {
  2. public static void main(String[] args){
  3. ClassLoader classLoader = MainClass.class.getClassLoader();
  4. try {
  5. Class aClass = classLoader.loadClass("com.jenkov.MyClass");
  6. System.out.println("aClass.getName() = " + aClass.getName());
  7. } catch (ClassNotFoundException e) {
  8. e.printStackTrace();
  9. }
  10. }

动态类重载

动态类重载有一点复杂。Java 内置的类加载器在加载一个类之前会检查它是否已经被加载。
因此重载一个类是无法使用 Java 内置的类加载器的,如果想要重载一个类你需要手动继承 ClassLoader。
在你定制 ClassLoader 的子类之后,你还有一些事需要做。所有被加载的类都需要被链接。这个过程是通过 ClassLoader.resolve() 方法来完成的。
由于这是一个 final 方法,因此这个方法在 ClassLoader 的子类中是无法被重写的。
resolve() 方法是不会允许给定的 ClassLoader 实例链接一个类两次。
所以每当你想要重载一个类的时候你都需要使用一个新的 ClassLoader 的子类。你在设计类重载功能的时候这是必要的条件。

自定义类重载

在前面已经说过你不能使用已经加载过类的类加载器来重载一个类。因此你需要其他的 ClassLoader 实例来重载这个类。但是这又带来了一些新的挑战。
所有被加载到 Java 应用中的类都以类的全名(包名 + 类名)作为一个唯一标识来让 ClassLoader 实例来加载。这意味着,类 MyObject 被类加载器 A 加载,如果类加载器 B 又加载了 MyObject 类,那么两个加载器加载出来的类是不同的。