类加载器混淆

10

我在几个地方看到过"Class.getClassLoader()返回用于加载该特定类的ClassLoader",因此,我对以下示例的结果感到困惑:


package test;

import java.lang.*;

public class ClassLoaders { 
    public static void main(String[] args) throws java.lang.ClassNotFoundException{
      MyClassLoader mcl = new MyClassLoader();
      Class clazz = mcl.loadClass("test.FooBar");
      System.out.println(clazz.getClassLoader() == mcl); // prints false
      System.out.println(clazz.getClassLoader()); // prints e.g. sun.misc.Launcher$AppClassLoader@553f5d07
    }
}

class FooBar { }

class MyClassLoader extends ClassLoader { }

语句clazz.getClassLoader() == mcl不应该返回true吗?有人可以解释一下我错在哪里了吗?

谢谢。


如果mcl是普通的Webapp类加载器并且test.FooBar类对于Webapp可用,那么它将按预期工作,因为Webapp类加载器会反转java.lang之外的任何内容的优先级。 - Hugo
很想看看你是如何编写MyClassLoader的。我在http://tshrestha.blogspot.com/2011/12/using-custom-classloader.html上有一个例子。 - Ustaman Sangat
3个回答

17

每当您创建自己的类加载器时,它将附加在类加载器树形层次结构中。为了加载一个类,类加载器首先将加载委托给其父级。只有在所有父级都没有找到该类时,最初被要求加载类的加载器才会尝试进行加载。

在您的特定情况下,加载被委托给父类加载器。虽然您要求您的MyClassLoader进行加载,但实际上是父类加载器进行加载。在这种情况下,它是AppClassLoader。


添加以下代码:System.out.println(clazz.getClassLoader() == mcl.getParent()); //返回true...明白了,这很好证明了这一点,谢谢。 - Newtopian

6
引用ClassLoader的API文档
每个ClassLoader实例都有一个关联的父ClassLoader。当请求查找类或资源时,ClassLoader实例会在尝试查找类或资源之前将其委托给其父ClassLoader进行查找。

还有,该示例使用no-args类加载器构造函数:“使用getSystemClassLoader()方法返回的ClassLoader作为父类加载器创建一个新的类加载器。”(奇怪的向后兼容行为。) - Tom Hawtin - tackline

-2

如果自定义类加载器将调用委托给VM的类加载器,那么谁来加载类。clazz.getClassLoader()将返回此类加载器。

进入细节:类ClassLoader的Javadoc提供了以下执行步骤顺序的解释:

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

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

由于您未更改方法而继承了它们,因此此行为将保持不变。第2步将是加载类的步骤。由于您调用ClassLoader的无参数构造函数(自动地,因为您没有在MyClassLoader中定义构造函数),因此您自动使用内置的ClassLoader。


实际上,它首先要求其父级在尝试自己查找类之前查找类。 - Michael Borgwardt
迈克尔,Java SE是这样定义的,但Java EE建议实现类加载器并进行其他操作... - Tom Hawtin - tackline

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接