Java中的ClassLoader

ClassLoader in Java

我正在使用Java中的类加载器,发现以下行为。我可以从逻辑上对此进行推理,但是我不确定我所假设的是完全正确的。我想知道这种行为的更正式解释。

我在想什么?
所以我有以下代码:

1
2
3
4
5
6
URL[] classURLs = {new URL("file://C:/Users/HP/IdeaProjects/test/out/production/test/")};
URLClassLoader urlClassLoader = new URLClassLoader(classURLs, null);
Class< ? > personClass = urlClassLoader.loadClass("com.test.Person");

// the following line will give a ClassCastException
Person p = (Person) personClass.getDeclaredConstructor().newInstance();

现在,最后一行给了我ClassCastException

我对为什么得到ClassCastException的推理(猜测):personClass的类加载器是urlClassLoader,而Person类的类加载器实际上是应用程序类加载器或系统类加载器(如果我需要我错了)。这些类加载器不匹配,我得到了ClassCastException。 (我在这里假设在类型转换时对类加载器执行检查)

所以现在我继续以下列方式探索和改变urlClassLoader的构造:

1
URLClassLoader urlClassLoader = new URLClassLoader(classURLs, Main.class.getClassLoader());

这里Main是封闭类。上一行使我免于ClassCastException

我对此的推理(猜测):由于现在urlClassLoader具有应用程序类加载器作为其父级(此应用程序类加载器与用于加载Person类的加载器相同),因此在尝试进行强制转换时,Java检查了类加载器是否匹配并且在向上进行类加载器匹配并且没有ClassCastException之后,继续使用urlClassLoader的父级进行检查。

我假设将要类型转换的对象的类的类加载器已与需要类型转换的类的类加载器进行了检查,并且如果该类加载器与对象类的类加载器的父类不匹配,则尝试进行匹配并且这继续。

如果我在任何时候错了,请纠正我,并提供指向此行为的正式文档的指针。

我已经看到了此链接,但这未提供我所要求的详细信息。


您观察到的行为的正式文档在ClassLoader#loadClass()文档中:

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

  • Invoke findLoadedClass(String) to check if the class has already been loaded.

  • 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.

  • Invoke the findClass(String) method to find the class.

  • 如果指定父类加载器,则URLClassLoader会在尝试加载类本身之前检查父类加载器是否包含该类,这意味着它将从您的应用程序类路径中找到该类。

    因此,如果您设置父类加载器,则此行:

    1
    Class< ? > personClass = urlClassLoader.loadClass("com.test.Person");

    表现与

    1
    Class< ? > personClass = Main.class.getClassLoader().loadClass("com.test.Person");

    如果类com.test.Person在应用程序类加载器上可用(必须为它,否则不能加载您的Main类)。


    您正在动态加载类,因此,由于您能够编译类" Person",因此这意味着您将两次加载同一类,从而导致类强制转换异常。

    从类路径中删除该库,但是不会收到此错误,您也将失去对Person对象的访问。

    当您加载它时它仍然存在,但是访问它的方式将是通过Reflection,并且您必须将" Person"对象存储为" Object"。