java - 当我使用自定义类加载器时出现 ClassCastException

标签 java jvm classloader classcastexception dynamic-class-loaders

我尝试了解 java 类加载。我写了例子:

class XLoader extends ClassLoader {

    // карта отображения имен классов на файлы .class, где хранятся их определения
    HashMap<String, String> mappings;

    XLoader(HashMap mappings) {
        this.mappings = mappings;
    }


    public synchronized Class loadClass(String name) throws ClassNotFoundException {
        try {
            // важно!
            // приоритет отдан именно загрузке с помощью встроенного загрузчика

            if (!mappings.containsKey(name)) {
                System.out.println("loadClass (" + name + ") with parent classloader");
                return super.findSystemClass(name);
            }
            System.out.println("loadClass (" + name + ") with XLoader");
            String fileName = mappings.get(name);
            FileInputStream fin = new FileInputStream(fileName);
            byte[] bbuf = new byte[(int) (new File(fileName).length())];
            fin.read(bbuf);
            fin.close();
            return defineClass(name, bbuf, 0, bbuf.length);

        } catch (IOException e) {
            e.printStackTrace();
            throw new ClassNotFoundException(e.getMessage(), e);
        }
    }
}

我还有接口(interface):

public interface ISexyInterface {

    public void makeBar ();

}

和类:

public class SexyClassForLoader implements ISexyInterface {
  ...
}

主要方法:

public static void main(String[] args) throws Exception {

        HashMap<String, String> mappings = new HashMap();
        mappings.put("sur.che.SexyClassForLoader", "D:\\java_things\\custom-classloader\\out\\production\\custom-classloader1\\sur\\che\\SexyClassForLoader.class");
        // if comment this line you will see
        //mappings.put("sur.che.ISexyInterface", "D:\\java_things\\custom-classloader\\out\\production\\custom-classloader1\\sur\\che\\ISexyInterface.class");

        XLoader xloa = new XLoader(mappings);
        Class sexy_cla = xloa.loadClass("sur.che.SexyClassForLoader");
        System.out.println("class was loaded with " + sexy_cla.getClassLoader());

        Object sexy_ob = sexy_cla.newInstance();
        System.out.println(sexy_ob.getClass().getClassLoader());
        System.out.println(ISexyInterface.class.getClassLoader());
        Thread.sleep(100);
        ISexyInterface local_sexy = (ISexyInterface) sexy_ob;
        local_sexy.makeBar();
    }

此示例运行良好并产生以下输出:

loadClass (sur.che.SexyClassForLoader) with XLoader
loadClass (sur.che.ISexyInterface) with parent classloader
loadClass (java.lang.Object) with parent classloader
class was loaded with sur.che.XLoader@7f31245a
loadClass (java.lang.System) with parent classloader
loadClass (java.io.PrintStream) with parent classloader
SexyClassForLoader$$static
SexyClassForLoader$$init
sur.che.XLoader@7f31245a
sun.misc.Launcher$AppClassLoader@232204a1
make bar

但是让我们一起玩:

    //mappings.put("sur.che.ISexyInterface", "D:\\java_things\\custom-classloader\\out\\production\\custom-classloader1\\sur\\che\\ISexyInterface.class");

如果取消注释这一行,我会看到以下输出:

loadClass (sur.che.SexyClassForLoader) with XLoader
loadClass (sur.che.ISexyInterface) with XLoader
loadClass (java.lang.Object) with parent classloader
class was loaded with sur.che.XLoader@7f31245a
loadClass (java.lang.System) with parent classloader
loadClass (java.io.PrintStream) with parent classloader
SexyClassForLoader$$static
SexyClassForLoader$$init
sur.che.XLoader@7f31245a
sun.misc.Launcher$AppClassLoader@232204a1
Exception in thread "main" java.lang.ClassCastException: sur.che.SexyClassForLoader cannot be cast to sur.che.ISexyInterface
    at sur.che.Loader.main(Loader.java:25)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at com.intellij.rt.execution.application.AppMain.main(AppMain.java:144)

请解释这种行为。

附言

我知道如果同一个类加载了 2 个不同的加载器。它算作 2 个不同的类。

最佳答案

If uncomment this line I see following output:......

Class sexy_cla = xloa.loadClass("sur.che.SexyClassForLoader");

sur.che.SexyClassForLoaderXloader 加载,因为它有一个接口(interface) sur.che.ISexyInterface 然后是 jvm 将使用 sur.che.SexyClassForLoaderclassloader 加载此接口(interface)。
但是对于这个声明:

System.out.println(ISexyInterface.class.getClassLoader());

因为包含main方法AppLoader加载,所以jvm会使用AppLoader加载< em>ISexyInterface.class.

 ISexyInterface local_sexy = (ISexyInterface) sexy_ob;

很明显,这个ISexyInterface的类加载器是AppLoader,但是SexyClassForLoader的内部依赖(ISexyInterface)是由加载的xLoader。换句话说,jvm 中有两个 ISexyInterface.class,一个由Xloader 加载,另一个由App sexy_ob 实现了由 Xloader 加载的 ISexyInterface.class。您尝试将 sexy_ob 转换为 ISexyInterface 类型(由 App 加载)。 所以 castException

如果注释,只有一个 ISexyInterface .classApp 加载。所以一切正常。

关于java - 当我使用自定义类加载器时出现 ClassCastException,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41610912/

相关文章:

java - 缓存图像和数据时应该使用内部存储还是外部存储?

java - 具有翻转数值的 Android 希伯来语 RTL 字符串

java - 尝试从列表中的jsp中实现jSTL

java - JVM 中的段错误导致堆栈溢出,但仅限于 VMWare

java - Eclipse 和 Intellij 不断因 EXCEPTION_ACCESS_VIOLATION 崩溃

java - 如果两个库的类具有相同的类和包名称供其内部使用

java - 创建 2 个可运行的 JAR,它们使用相同的库而不需要它们两次

java - Java 中的每个用户定义的 Class 是否都有一个与之关联的类 Class 对象?那么它和Reflection有什么不同呢?

java - 在 java web 应用程序中加载 xml 文件

Java:当类加载器加载 DLL 时,它们存储在内存中的什么位置?