为什么在第一次调用loadClass方法时,此引用是AppClassLoader而不是MyClassLoader

时间:2017-01-03 04:41:18

标签: java jvm classloader

我通过扩展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 < / strong>也就是说引用是 sun.misc.Launcher $ AppClassLoader 而不是 com.classLoader.MyClassLoader@xxxxxx

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

  

它会将AppClassLoader设置为默认构造函数中MyClassLoader的父级。

我通过

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

找到 MyClassLoader的父确实是 AppClassLoader 它是如何实现这一点的,我的默认构造函数什么都不做,这是在构造函数方法中实现的它的父类 ClassLoader ?我读了它的父构造函数,仍然无法解决。

无论如何,当我们第一次调用 loadClass 时,似乎无法解释为什么这个 AppClassLoader 而不是 MyClassLoader 方法。 我想这是因为* MyClassLoader的classLoader是AppClassLoader,但这只是一种我没有根据的直觉。

。一切都将受到赞赏。

1 个答案:

答案 0 :(得分:0)

嗯,这些都记录在案:

ClassLoader() constructor
  

使用方法ClassLoader返回的getSystemClassLoader()作为父类加载器创建新的类加载器。

loadClass(String)
  

使用指定的二进制名称加载类。 ...调用此方法相当于调用loadClass(name, false)

loadClass(String,boolean-)
  

使用指定的二进制名称加载类。此方法的默认实现按以下顺序搜索类:

     
      
  1. 调用findLoadedClass(String)以检查该类是否已加载。
  2.   
  3. 在父类加载器上调用loadClass方法。如果父级是null,则使用内置于虚拟机的类加载器。
  4.   
  5. 调用findClass(String)方法查找班级。
  6.         

    如果使用上述步骤找到该类,并且resolve标志为true,则此方法将在生成的resolveClass(Class)对象上调用Class方法。

         

    鼓励ClassLoader的子类覆盖findClass(String),而不是此方法。

class documentation of ClassLoader中也提到了这一点:

  

ClassLoader类使用委派模型来搜索类和资源。 ClassLoader的每个实例都有一个关联的父类加载器。当请求查找类或资源时,ClassLoader实例会在尝试查找类或资源本身之前将对类或资源的搜索委托给其父类加载器。虚拟机的内置类加载器(称为“引导类加载器”)本身不具有父级,但可以作为ClassLoader实例的父级。

此逻辑确保干净的作用域,即父作用域的类不引用子作用域的类,并且同一限定名的类之间没有冲突,但是由不同的类加载器定义。此类可能仍存在于不同的范围中,但不存在于嵌套范围中。但类加载器并未强制遵循此规则。委托模型是用Java 2引入的。