浏览 2437 次
精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
|
|
---|---|
作者 | 正文 |
发表时间:2011-08-13
参照下面的地址的文章:深入探讨 Java 类加载器 其中讲到下面的这段: 引用 类加载器的代理模式 类加载器在尝试自己去查找某个类的字节代码并定义它时,会先代理给其父类加载器,由父类加载器先去尝试加载这个类,依次类推。在介绍代理模式背后的动机之前,首先需要说明一下 Java 虚拟机是如何判定两个 Java 类是相同的。Java 虚拟机不仅要看类的全名是否相同,还要看加载此类的类加载器是否一样。只有两者都相同的情况,才认为两个类是相同的。即便是同样的字节代码,被不同的类加载器加载之后所得到的类,也是不同的。比如一个 Java 类 com.example.Sample,编译之后生成了字节代码文件 Sample.class。两个不同的类加载器 ClassLoaderA 和 ClassLoaderB 分别读取了这个 Sample.class 文件,并定义出两个 java.lang.Class 类的实例来表示这个类。这两个实例是不相同的。对于 Java 虚拟机来说,它们是不同的类。试图对这两个类的对象进行相互赋值,会抛出运行时异常 ClassCastException。下面通过示例来具体说明。代码清单 3 中给出了 Java 类 com.example.Sample。 其中清单3的代码如下: package com.example; public class Sample { private Sample instance; public void setSample(Object instance) { this.instance = (Sample) instance; } } 一个测试方法如下: 即代码清单5. public void testClassIdentity() { String classDataRootPath = "C:\\workspace\\Classloader\\classData"; FileSystemClassLoader fscl1 = new FileSystemClassLoader(classDataRootPath); FileSystemClassLoader fscl2 = new FileSystemClassLoader(classDataRootPath); String className = "com.example.Sample"; try { Class<?> class1 = fscl1.loadClass(className); Object obj1 = class1.newInstance(); Class<?> class2 = fscl2.loadClass(className); Object obj2 = class2.newInstance(); Method setSampleMethod = class1.getMethod("setSample", java.lang.Object.class); setSampleMethod.invoke(obj1, obj2); } catch (Exception e) { e.printStackTrace(); } } 文章对此的测试结果是如下: 引用 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 classloader.ClassIdentity.testClassIdentity(ClassIdentity.java:26) at classloader.ClassIdentity.main(ClassIdentity.java:9) Caused by: java.lang.ClassCastException: com.example.Sample cannot be cast to com.example.Sample at com.example.Sample.setSample(Sample.java:7) ... 6 more 结果解释,如上,和下面: 引用 从 代码清单 5 给出的运行结果可以看到,运行时抛出了 java.lang.ClassCastException 异常。虽然两个对象 obj1 和 obj2 的类的名字相同,但是这两个类是由不同的类加载器实例来加载的,因此不被 Java 虚拟机认为是相同的。 了解了这一点之后,就可以理解代理模式的设计动机了。代理模式是为了保证 Java 核心库的类型安全。所有 Java 应用都至少需要引用 java.lang.Object 类,也就是说在运行的时候,java.lang.Object 这个类需要被加载到 Java 虚拟机中。如果这个加载过程由 Java 应用自己的类加载器来完成的话,很可能就存在多个版本的 java.lang.Object 类,而且这些类之间是不兼容的。通过代理模式,对于 Java 核心库的类的加载工作由引导类加载器来统一完成,保证了 Java 应用所使用的都是同一个版本的 Java 核心库的类,是互相兼容的。 不同的类加载器为相同名称的类创建了额外的名称空间。相同名称的类可以并存在 Java 虚拟机中,只需要用不同的类加载器来加载它们即可。不同类加载器加载的类之间是不兼容的,这就相当于在 Java 虚拟机内部创建了一个个相互隔离的 Java 类空间。这种技术在许多框架中都被用到,后面会详细介绍。 但是, 但是我测试在机子上测试的时候,运行了N多次都没有见到,异常出来,我是第一次希望出现异常, 但是没有出现. 我测试代码如下,其它的没有变,就是包名变了,和一些路径变了.: package guet.dream.jvm; public class Sample { private Sample instance; public void setSample(Object instance){ this.instance = (Sample)instance; } } 测试方法: package guet.dream.jvm; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; public class ClassIndentityTest { /** * @param args */ public static void main(String[] args) { String classDataRootPath = "guet/dream/jvm/"; FileSystemClassLoader fscl1 = new FileSystemClassLoader(classDataRootPath); FileSystemClassLoader fscl2 = new FileSystemClassLoader(classDataRootPath); String className = "guet.dream.jvm.Sample"; try { Class<?> clazz1 = fscl1.loadClass(className); Class<?> clazz2 = fscl2.loadClass(className); Object obj1 = clazz1.newInstance(); Object obj2 = clazz2.newInstance(); Method setSampleMethod = clazz1.getMethod("setSample", Object.class); setSampleMethod.invoke(obj1, obj2); } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (SecurityException e) { e.printStackTrace(); } catch (NoSuchMethodException e) { e.printStackTrace(); } catch (IllegalArgumentException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } } } 下面是原文中,我稍加修改的ClassLoader. package guet.dream.jvm; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; public class FileSystemClassLoader extends ClassLoader { private String rootDir; public FileSystemClassLoader(String rootDir){ this.rootDir = rootDir; } @Override protected Class<?> findClass(String name) throws ClassNotFoundException{ byte[] classData = getClassData(name); if(classData == null){ throw new ClassNotFoundException(); }else{ return defineClass(name, classData, 0, classData.length); } } private byte[] getClassData(String className) { String path = classNameToPath(className); InputStream in; try { in = new FileInputStream(path); ByteArrayOutputStream baos = new ByteArrayOutputStream(); int bufferSize = 4096; byte[] buffer = new byte[bufferSize]; int bytesNumRead = 0; while((bytesNumRead = in.read(buffer)) != -1 ){ baos.write(buffer,0,bytesNumRead); } return baos.toByteArray(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } return null; } private String classNameToPath(String className) { return rootDir+File.separatorChar+className.replace('.', File.separatorChar)+".class"; } } 运行结果没有任何异常,希望,有对这方面了解的,各位网友,测试或者给解答下. 这好像是我发的第一个贴吧! 我的JDK信息如下: 引用 banxi1988@banxi:~$ java -version java version "1.6.0_22" OpenJDK Runtime Environment (IcedTea6 1.10.2) (6b22-1.10.2-0ubuntu1~11.04.1) OpenJDK Server VM (build 20.0-b11, mixed mode) banxi1988@banxi:~$ 声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
推荐链接
|
|
返回顶楼 | |
发表时间:2011-08-15
居然没有知道了解的人回复一个啊!
嗯,改天我知道了解之后,我自己来回复吧. 也许也有人碰到与我同样的问题呢! |
|
返回顶楼 | |
发表时间:2011-08-15
有可能是你的Sample类被AppClassloader给加载了,因为如果
FileSystemClassLoader的父类加载器是AppClassloader的话,根据委托模型,会由父类加载器进行加载,FileSystemClassLoader就没有机会进行加载了。因此,可以这样: public FileSystemClassLoader(String rootDir){ super(null);//设置父类加载器是Bootstrap Clasloader this.rootDir = rootDir; } |
|
返回顶楼 | |
发表时间:2011-08-15
先用getClass.getClassLoader()看这两个实例所对应类的ClassLoader是否相同,不就OK了。
懒得看代码了。 |
|
返回顶楼 | |
发表时间:2011-08-18
看一下你的父级到root类加载器的CLASSPATH是否包含这个类
|
|
返回顶楼 | |