谈谈Java虚拟机2――类加载器 (三)

2014-11-24 03:19:46 · 作者: · 浏览: 2
,b.length);

} 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 核心库的类,是互相兼容的。

线程上下文类加载器