} catch (IOException e) {
e.printStackTrace();
}
return super.loadClass(name);
}
}
代码很简单,就是加载与MyClassLoader同目录下的class类文件。
写一个辅助类
package com.xiaoruoen.test;
public class Sample {
private Sample instance;
public void setSample(Object obj){
this.instance = (Sample)obj;
}
}
写一个测试的类:
package com.xiaoruoen.test;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class ClassLoaderTest {
/**
* @param args
* @throws ClassNotFoundException
* @throws IllegalAccessException
* @throws InstantiationException
* @throws NoSuchMethodException
* @throws SecurityException
* @throws InvocationTargetException
* @throws IllegalArgumentException
*/
public static void main(String[] args) throws InstantiationException, IllegalAccessException, ClassNotFoundException, SecurityException, NoSuchMethodException, IllegalArgumentException, InvocationTargetException {
MyClassLoader loader1 = new MyClassLoader();
MyClassLoader loader2 = new MyClassLoader();
Class class1 = loader1.loadClass("com.xiaoruoen.test.Sample");
Class class2 = loader2.loadClass("com.xiaoruoen.test.Sample");
Object obj1 = class1.newInstance();
Object obj2 = class2.newInstance();
Object obj3 = loader1.loadClass("com.xiaoruoen.test.ClassLoaderTest");
System.out.println(obj3);
System.out.println(obj3 instanceof com.xiaoruoen.test.ClassLoaderTest);
Method method = class1.getMethod("setSample", java.lang.Object.class);
method.invoke(obj1, obj2);
}
}
上面代码运行的结果为:
class com.xiaoruoen.test.ClassLoaderTest
false
Exception in thread "main" java.lang.reflect.InvocationTargetException
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at com.xiaoruoen.test.ClassLoaderTest.main(ClassLoaderTest.java:29)
Caused by: java.lang.ClassCastException: com.xiaoruoen.test.Sample cannot be cast to com.xiaoruoen.test.Sample
at com.xiaoruoen.test.Sample.setSample(Sample.java:8)
... 5 more
从给出的结果来看:
给出的运行结果可以看到,运行时抛出了java.lang.ClassCastException异常。虽然两个对象obj1和obj2的类的名字相同,但是这两个类是由不同的类加载器实例来加载的,因此不被Java 虚拟机认为是相同的。同样,我们使用了MyClassLoader加载了一个名为com.xiaoruoen.test.ClassLoaderTest的类,并实例化了这个类的对象。两行输出结果中,从第一句可以看到这个对象确实是类com.xiaoruoen.test.ClassLoaderTest实例化出来的对象,但从第二句可以发现这个对象与类com.xiaoruoen.test.ClassLoaderTest做所属类型检查的时候却返回了false,这是因为虚拟机中存在了两个ClassLoaderTest类,一个是由系统应用程序类加载器加载的,一个是由我们自定义的类加载器加载的,虽然都是来自同一个Class文件,但依然是两个独立的类,做对象所属类型检查时结果自然是false。
了解了这一点之后,就可以理解代理模式的设计动机了。
代理模式是为了保证Java 核心库的类型安全。所有Java 应用都至少需要引用java.lang.Object类,也就是说在运行的时候,java.lang.Object这个类需要被加载到Java 虚拟机中。如果这个加载过程由Java 应用自己的类加载器来完成的话,很可能就存在多个版本的java.lang.Object类(由上面的例子就可以看出来),而且这些类之间是不兼容的。通过代理模式,对于Java 核心库的类的加载工作由引导类加载器来统一完成,保证了Java 应用所使用的都是同一个版本的Java 核心库的类,是互相兼容的。
线程上下文类加载器