首页 | 源码下载 | 网站模板 | 网页特效 | 广告代码 | 网页素材 | 字体下载 | 书库 | 站长工具
会员投稿 投稿指南 RSS订阅
当前位置:主页>网络编程>java教程>资讯:深入探讨 Java 类加载器

深入探讨 Java 类加载器

www.jz123.cn  2010-03-22   来源:   中国建站    责任编辑(袁袁)    我要投递新闻

  类加载器的代理模式

  类加载器在尝试自己去查找某个类的字节代码并定义它时,会先代理给其父类加载器,由父类加载器先去尝试加载这个类,依次类推。在介绍代理模式背后的动机之前,首先需要说明一下 Java 虚拟机是如何判定两个 Java 类是相同的。Java 虚拟机不仅要看类的全名是否相同,还要看加载此类的类加载器是否一样。只有两者都相同的情况,才认为两个类是相同的。即便是同样的字节代码,被不同的类加载器加载之后所得到的类,也是不同的。比如一个 Java 类 com.example.Sample,编译之后生成了字节代码文件 Sample.class。两个不同的类加载器 ClassLoaderA 和 ClassLoaderB 分别读取了这个 Sample.class 文件,并定义出两个 java.lang.Class 类的实例来表示这个类。这两个实例是不相同的。对于 Java 虚拟机来说,它们是不同的类。试图对这两个类的对象进行相互赋值,会抛出运行时异常 ClassCastException。下面通过示例来具体说明。代码清单 3 中给出了 Java 类 com.example.Sample。

  清单 3. com.example.Sample 类

  package com.example;

  public class Sample {

  private Sample instance;

  public void setSample(Object instance) {

  this.instance = (Sample) instance;

  }

  }

  如 代码清单 3 所示,com.example.Sample 类的方法 setSample 接受一个 java.lang.Object 类型的参数,并且会把该参数强制转换成 com.example.Sample 类型。测试 Java 类是否相同的代码如 代码清单 4 所示。

  清单 4. 测试 Java 类是否相同

  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();

  }

  }

  代码清单 4 中使用了类 FileSystemClassLoader 的两个不同实例来分别加载类 com.example.Sample,得到了两个不同的 java.lang.Class 的实例,接着通过 newInstance() 方法分别生成了两个类的对象 obj1 和 obj2,最后通过 Java 的反射 API 在对象 obj1 上调用方法 setSample,试图把对象 obj2 赋值给 obj1 内部的 instance 对象。代码清单 4 的运行结果如 代码清单 5 所示。

  清单 5. 测试 Java 类是否相同的运行结果

  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 类空间。这种技术在许多框架中都被用到,后面会详细介绍。

  下面具体介绍类加载器加载类的详细过程。

上一篇:设计模式之java接口和java抽象类 下一篇:一道微软面试题的Java解法

评论总数:0 [ 查看全部 ] 网友评论


关于我们隐私版权广告服务友情链接联系我们网站地图