自定义类加载器加载类的问题

本贴最后更新于 2934 天前,其中的信息可能已经事过景迁

一、创建自定义类加载器

  自定义了一个类加载器 MyClassLoader,其中实现是通过读取 class 文件为字节数组,然后通过 ClassLoader 中的 defineClass(String name,byte[],int off,int len)把字节数组中的内容转换成 Java 类,返回的是 Class 类的实例,代码如下:

package priv.leopold.tool; import java.io.DataInputStream; import java.io.File; import java.io.FileInputStream; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; public class MyClassLoader extends ClassLoader { public Class<?> load(File classFile) throws ClassNotFoundException { byte[] classData = getClassData(classFile); if (classData == null) { throw new ClassNotFoundException(); } else { return defineClass(null, classData, 0, classData.length); } } private byte[] getClassData(File classFile) { try { InputStream ins = new FileInputStream(classFile); ByteArrayOutputStream baos = new ByteArrayOutputStream(); int bufferSize = 4096; byte[] buffer = new byte[bufferSize]; int bytesNumRead = 0; while ((bytesNumRead = ins.read(buffer)) != -1) { baos.write(buffer, 0, bytesNumRead); } ins.close(); return baos.toByteArray(); } catch (IOException e) { e.printStackTrace(); } return null; } }

二、测试自定义类加载器

先创建两个 MyClassLoader 实例:

MyClassLoader loader1 = new MyClassLoader(); MyClassLoader loader2 = new MyClassLoader();

然后通过这两个类加载器的实例加载同一个类,并通过 Class 对象创建实例:

Class<?> class1 = loader1.load(classFile); //第一个类加载器加载类 Object obj1 = class1.newInstance(); Object obj2 = class1.newInstance(); Class<?> class2 = loader2.load(classFile); //第二个类加载器加载类 Object obj3 = class2.newInstance();

接下来执行类中的方法:

Method method = class1.getMethod("setInstance", Object.class); method.invoke(obj1, obj2); method.invoke(obj1, obj3);

其中测试的类为 Sample,代码如下:

public class Sample { private Sample instance; public Sample getInstance() { return instance; } public void setInstance(Object instance) { this.instance = (Sample) instance; } }

执行上面的代码后会抛出异常:

Exception in thread "main" java.lang.reflect.InvocationTargetException at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source) at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source) at java.lang.reflect.Method.invoke(Unknown Source) at test.Test.main(Test.java:28) Caused by: java.lang.ClassCastException: bean.Sample cannot be cast to bean.Sample at bean.Sample.setInstance(Sample.java:12) ... 5 more

    引起上面异常的是这句代码:this.instance = (Sample) instance;
虽然是用同一个类文件(这里用的是 Sample.class 文件)加载出来的 Class 对象,但通过上面的这句代码抛出的异常可以发现,它们创建的实例属于不同类型(即使这里都叫做 bean.Sample)。

三、分析原因

    对于某个特定的类加载器来说,一个 Java 类只能被载入一次,也就是说在 Java 虚拟机中,类的完整标识是(classLoader,package,className)。
另外一个类可以被不同的类加载器加载,但是,同一个类,由不同的类加载器实例加载的话,会在方法区产生两个不同的类,彼此不可见,并且在堆中生成不同 Class 实例。
    如果用的类加载器没有重写 ClassLoader 中的 getSystemClassLoader 方法,那么用的都是 AppClassLoader 的同一个实例进行类的加载的,所以加载同一个类在内存中只有一个类,即所有通过正常双亲委派模式的类加载器加载的 classpath 下的和 ext 下的所有类在方法区都是同一个类,堆中的 Class 实例也是同一个。

  • Java

    Java 是一种可以撰写跨平台应用软件的面向对象的程序设计语言,是由 Sun Microsystems 公司于 1995 年 5 月推出的。Java 技术具有卓越的通用性、高效性、平台移植性和安全性。

    3201 引用 • 8217 回帖

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...