为什么第一次调用 loadClass 方法时这个引用是 AppClassLoader 而不是 MyClassLoader

why this reference is AppClassLoader rather than MyClassLoader when invoke loadClass method at the first time

我通过扩展 classLoader 并覆盖其 findClass 方法来编写 myOwnClassLoader。遇到一个有趣的问题:

这里是代码段:

public class MyClassLoader extends ClassLoader {
    @Override
    public Class<?> findClass(String name) {
        byte[] bt = loadClassData(name);
        return defineClass(name, bt, 0, bt.length);
    }

    private byte[] loadClassData(String className) {
        ...
    }

    public static void main(String[] args) throws ClassNotFoundException{
       MyClassLoader myClassLoader = new MyClassLoader();
       //Class<?> clazz = myClassLoader.findClass("com.classLoader.BMW");
       Class<?> clazz = myClassLoader.loadClass("com.classLoader.BMW");
       System.out.println(clazz.getClassLoader());//////////////////
    }
}

执行时

Class<?> clazz = myClassLoader.findClass("com.classLoader.BMW");

输出符合我的预期: 输出:

com.classLoader.MyClassLoader@xxxxxx

但是执行时

Class<?> clazz = myClassLoader.loadClass("com.classLoader.BMW");

输出超出了我的想象: 输出:

sun.misc.Launcher$AppClassLoader@xxxx

预期输出

com.classLoader.MyClassLoader@xxxxxx

这里是ClassLoader

loadClass方法的代码段
 Class<?> c = findLoadedClass(name);
 if (c == null) {
    long t0 = System.nanoTime();
    try {
        if (parent != null) {
            c = parent.loadClass(name, false);
            } else {
            c = findBootstrapClassOrNull(name);
        }
   .....

我调试它并发现当我们在 第一次 调用 loadClass 时,父级是 sun.misc.Launcher$ExtClassLoader@6aa8ceb6 也就是说 this 引用是 sun.misc.Launcher$AppClassLoader 而不是com.classLoader.MyClassLoader@xxxxxx.


我通过互联网和博客搜索 define own classLoader 告诉

it will set AppClassLoader as parent of MyClassLoader in default constructor.

我通过

查看
System.out.println(myClassLoader.getParent());

找到 MyClassLoader 的父级确实是 AppClassLoader 它是如何实现的,我的默认构造函数什么都不做,这是在 其父class的构造方法 ClassLoader?我阅读了它的父构造函数,但仍然无法弄清楚。

无论如何,这似乎无法解释为什么 thisAppClassLoader 而不是 MyClassLoader我们首先调用 loadClass 方法。 我想这是因为 *MyClassLoader 的 classLoader 是 AppClassLoader 但这只是我没有要点的直觉。


万事如意。

嗯,都记录在案了:

ClassLoader() constructor:

Creates a new class loader using the ClassLoader returned by the method getSystemClassLoader() as the parent class loader.

loadClass(String):

Loads the class with the specified binary name. … Invoking this method is equivalent to invoking loadClass(name, false).

loadClass(String,boolean-):

Loads the class with the specified binary name. The default implementation of this method searches for classes in the following order:

  1. Invoke findLoadedClass(String) to check if the class has already been loaded.
  2. Invoke the loadClass method on the parent class loader. If the parent is null the class loader built-in to the virtual machine is used, instead.
  3. Invoke the findClass(String) method to find the class.

If the class was found using the above steps, and the resolve flag is true, this method will then invoke the resolveClass(Class) method on the resulting Class object.

Subclasses of ClassLoader are encouraged to override findClass(String), rather than this method.

这个在class documentation of ClassLoader中也有提到:

The ClassLoader class uses a delegation model to search for classes and resources. Each instance of ClassLoader has an associated parent class loader. When requested to find a class or resource, a ClassLoader instance will delegate the search for the class or resource to its parent class loader before attempting to find the class or resource itself. The virtual machine's built-in class loader, called the "bootstrap class loader", does not itself have a parent but may serve as the parent of a ClassLoader instance.

这个逻辑确保干净的范围,即父范围的 class 不引用子范围的 class,并且 class 之间没有冲突具有相同限定名称的 es,但由不同的 class 加载器定义。这样的 classes 可能仍然存在于不同的范围内,但不存在于嵌套范围内。但是 class 加载器并没有强制遵循这个规则。 Java2.

引入了委派模型