Java虚拟机JVM学习06 自定义类加载器 父委托机制和命名空间的再讨论

创建用户自定义的类加载器

  要创建用户自定义的类加载器,只需要扩展java.lang.ClassLoader类,然后覆盖它的findClass(String name)方法即可,该方法根据参数指定的类的名字,返回对应的Class对象的引用。

自定义类加载器的例子

  代码:

  1. package com.mengdd.classloader;
  2.  
  3. import java.io.ByteArrayOutputStream;
  4. import java.io.File;
  5. import java.io.FileInputStream;
  6. import java.io.InputStream;
  7.  
  8. public class MyClassLoader extends ClassLoader {
  9.  
  10. private String name; // 类加载器的名字
  11. private String path = "d:\\"; // 加载类的路径
  12. private final String fileType = ".class"; // class文件的扩展名
  13.  
  14. public MyClassLoader(String name) {
  15. super(); // 让系统类加载器成为该类加载器的父加载器
  16. this.name = name;
  17. }
  18.  
  19. public MyClassLoader(ClassLoader parent, String name) {
  20. super(parent); // 显式指定该类加载器的父加载器
  21. this.name = name;
  22. }
  23.  
  24. @Override
  25. public String toString() {
  26. return this.name;
  27. }
  28.  
  29. public String getPath() {
  30. return path;
  31. }
  32.  
  33. public void setPath(String path) {
  34. this.path = path;
  35. }
  36.  
  37. @Override
  38. public Class<?> findClass(String name) throws ClassNotFoundException {
  39. // 重写的时候把protected改为public
  40.  
  41. // 获取字节数组
  42. byte[] data = this.loadClassData(name);
  43. // 将字节数组转换成Class对象返回
  44. return this.defineClass(name, data, 0, data.length);
  45.  
  46. }
  47.  
  48. /**
  49. * 得到class文件的二进制字节数组
  50. *
  51. * @param name
  52. * @return
  53. */
  54. private byte[] loadClassData(String name) {
  55.  
  56. InputStream is = null;
  57. byte[] data = null;
  58. ByteArrayOutputStream baos = null;
  59.  
  60. try {
  61.  
  62. // 将完整类名中的.转化成\
  63. name = name.replace(".", "\\");
  64. is = new FileInputStream(new File(path + name + fileType));
  65.  
  66. baos = new ByteArrayOutputStream();
  67. int ch = 0;
  68. while (-1 != (ch = is.read())) {
  69.  
  70. baos.write(ch);
  71. }
  72.  
  73. data = baos.toByteArray();
  74. }
  75. catch (Exception e) {
  76. e.printStackTrace();
  77. }
  78. finally {
  79. try {
  80. is.close();
  81. baos.close();
  82. }
  83. catch (Exception e2) {
  84. }
  85. }
  86. return data;
  87. }
  88.  
  89. // main方法用来测试
  90. public static void main(String[] args) throws Exception {
  91.  
  92. MyClassLoader loader1 = new MyClassLoader("loader1");
  93. // loader1的父加载器是系统类加载器
  94. // 系统类加载器会在classpath指定的目录中加载类
  95. loader1.setPath("d:\\myapp\\serverlib\\");
  96.  
  97. MyClassLoader loader2 = new MyClassLoader(loader1, "loader2");
  98. // 将loader1作为loader2的父加载器
  99. loader2.setPath("d:\\myapp\\clientlib\\");
  100.  
  101. MyClassLoader loader3 = new MyClassLoader(null, "loader3");
  102. // loader3的父加载器是根类加载器
  103. loader3.setPath("d:\\myapp\\otherlib\\");
  104.  
  105. // 测试加载
  106. test(loader2);
  107. test(loader3);
  108. System.out.println("test2---------------");
  109. // 测试不同命名空间的类的互相访问
  110. test2(loader3);
  111. }
  112.  
  113. public static void test(ClassLoader loader) throws Exception {
  114. Class clazz = loader.loadClass("com.mengdd.classloader.Sample");
  115.  
  116. Object object = clazz.newInstance();
  117.  
  118. }
  119.  
  120. public static void test2(ClassLoader loader) throws Exception {
  121. Class clazz = loader.loadClass("com.mengdd.classloader.Sample");
  122.  
  123. Sample object = (Sample) clazz.newInstance();
  124.  
  125. System.out.println("sample v1: " + object.v1);
  126.  
  127. }
  128. }

  其中Sample:

  1. package com.mengdd.classloader;
  2.  
  3. public class Sample {
  4.  
  5. public int v1 = 1;
  6.  
  7. public Sample() {
  8. System.out.println("Sample is loaded by: "
  9. + this.getClass().getClassLoader());
  10.  
  11. // 主动使用Dog类
  12. new Dog();
  13. }
  14. }

  Dog类:

  1. package com.mengdd.classloader;
  2.  
  3. public class Dog {
  4.  
  5. public Dog() {
  6. System.out.println("Dog is loaded by: "
  7. + this.getClass().getClassLoader());
  8. }
  9. }

  例子演示过程略,尝试把class文件放在不同的路径下,看输出或者报错结果。

  主要结论就是验证了父亲委托机制。

  采用loader1的时候由于其父类是系统类加载器(也即应用类加载器),所以如果可以在classpath中找到目标.class文件,则定义类加载器是系统类加载器,输出类似:

  sun.misc.Launcher$AppClassLoader@7448bc3d

  每个类加载器都有自己的命名空间,命名空间由该加载器及所有父加载器所加载的类组成。

  在Sample类中主动使用了Dog类,当执行Sample类的构造方法中的new Dog()语句时,Java虚拟机需要先加载Dog类,到底用哪个类加载器加载呢?

  从打印结果可以看出,Java虚拟机会用Sample类的定义类加载器去加载Dog类,加载过程也同样采用父亲委托机制

  如果Sample类首次主动使用Dog时,Sample类的加载器及它的父加载器都无法加载Dog类,将会抛出找不到文件的异常。

不同类加载器的命名空间关系

  同一个命名空间内的类是相互可见的,即可以互相访问。

  子加载器的命名空间包含所有父加载器的命名空间。

  因此由子加载器加载的类能看见父加载器加载的类。

  例如系统类加载器加载的类能看见根类加载器加载的类。

  由父加载器加载的类不能看见子加载器加载的类。

  可以理解为:由于子加载器中含有父加载器的引用,所以子加载器的范围更大

 

  如果两个加载器之间没有直接或间接的父子关系,那么它们各自加载的类相互不可见。

  比如这么一种情况:MyClassLoader类由系统类加载器加载,而Sample类由loader3类加载器加载,因此MyClassLoader类看不见Sample类。

  在MyClassLoader类的main()方法中使用Sample类,会导致错误。

  当两个不同命名空间内的类互相不可见时,可采用Java反射机制来访问对方实例的属性和方法,即反射可以突破命名空间的限制。

参考资料

  圣思园张龙老师Java SE视频教程。

  ClassLoader类:http://docs.oracle.com/javase/7/docs/api/

  相关博文:

  Java虚拟机JVM学习05 类加载器的父委托机制:http://www.cnblogs.com/mengdd/p/3562540.html

Java虚拟机JVM学习06 自定义类加载器 父委托机制和命名空间的再讨论的更多相关文章

  1. Java虚拟机JVM学习05 类加载器的父委托机制

    Java虚拟机JVM学习05 类加载器的父委托机制 类加载器 类加载器用来把类加载到Java虚拟机中. 类加载器的类型 有两种类型的类加载器: 1.JVM自带的加载器: 根类加载器(Bootstrap ...

  2. Java虚拟机JVM学习07 类的卸载机制

    Java虚拟机JVM学习07 类的卸载机制 类的生命周期 当Sample类被加载.连接和初始化后,它的生命周期就开始了. 当代表Sample类的Class对象不再被引用,即不可触及时,Class对象就 ...

  3. Java虚拟机JVM学习02 类的加载概述

    Java虚拟机JVM学习02 类的加载概述 类的加载 类的加载指的是将类的.class文件中的二进制数据读入到内存中,将其放在运行时数据区的方法区内,然后在堆区创建一个java.lang.Class对 ...

  4. Java虚拟机JVM学习01 流程概述

    Java虚拟机JVM学习01 流程概述 Java虚拟机与程序的生命周期 一个运行时的Java虚拟机(JVM)负责运行一个Java程序. 当启动一个Java程序时,一个虚拟机实例诞生:当程序关闭退出,这 ...

  5. 【JVM学习笔记】类加载器

    概述 类加载器用来把类加载到Java虚拟机中.从JDK1.2版本开始,类的加载过程采用父委托机制,这种机制能更好地保证Java平台的安全.在此委托机制中,除了Java虚拟机自带的根类加载器以外,其余的 ...

  6. java之jvm学习笔记四(安全管理器)

    java之jvm学习笔记四(安全管理器) 前面已经简述了java的安全模型的两个组成部分(类装载器,class文件校验器),接下来学习的是java安全模型的另外一个重要组成部分安全管理器. 安全管理器 ...

  7. Java虚拟机JVM学习04 类的初始化

    Java虚拟机JVM学习04 类的初始化 类的初始化 在初始化阶段,Java虚拟机执行类的初始化语句,为类的静态变量赋予初始值. 在程序中,静态变量的初始化有两种途径: 1.在静态变量的声明处进行初始 ...

  8. Java虚拟机JVM学习03 连接过程:验证、准备、解析

    Java虚拟机JVM学习03 连接过程:验证.准备.解析 类被加载后,就进入连接阶段. 连接就是将已经读入到内存的类的二进制数据合并到虚拟机的运行时环境中去. 连接阶段三个步骤:验证.准备和解析. 类 ...

  9. java类加载器学习2——自定义类加载器和父类委托机制带来的问题

    一.自定义类加载器的一般步骤 Java的类加载器自从JDK1.2开始便引入了一条机制叫做父类委托机制.一个类需要被加载的时候,JVM先会调用他的父类加载器进行加载,父类调用父类的父类,一直到顶级类加载 ...

随机推荐

  1. plsql导入excel时报错:ORA-01036: 非法变量名/编号

    导入oracle数据,选择工具->odbc导入->Excel 然后关于日期的插入出错,修改后如下:

  2. JAVA 设计模式 命令模式

    用途 命令模式 (Command) 将一个请求封装为一个对象,从而使你可以用不同的请求对客户进行参数化:对请求排队或请求日志,以及支持可撤销的操作. 命令模式是一种行为型模式. 结构

  3. 关于Entity Framework采用DB First模式创建后的实体批量修改相关属性技巧

    Entity Framework采用DB First模式创建实体是比较容易与方便的,修改已创建的实体在个数不多的情况下也是没问题的,但如果已创建的实体比较多,比如10个实体以上,涉及修改的地方比较多的 ...

  4. 用SQL语句查找包含有某个关键字的存储过程、触发器、函数等(仅适用MS SQL SERVER)

    第一种方法:利用系统表进行查询   --将text替换成你要查找的内容  select name  from sysobjects o, syscomments s  where o.id = s.i ...

  5. Swift 2.2发布

    Swift 2.2 发布了.支持linux平台.Swift是一种使用现代的安全设计方式和软件设计模式构建的通用编程语言.该版本语言更新如下: SE-0001: Allow (most) keyword ...

  6. 利用jstree插件轻松构建树应用

    最近完成了项目中的一个树状应用,第一次接触了jstree这个插件,总的来说它的官方文档还是比较详细的,但是在使用过程中还是出现了一些问题,下面我就来谈谈这款插件的使用和心得. 首先项目需要构建一棵树, ...

  7. Winform开发框架里面使用事务操作的原理及介绍

    在很多情况下,事务是个很有用的东西,可以把一系列的操作组合成一个原子粒度的操作,一旦组合中某个地方出错,可以整个干净的进行滚回,不会留下脏数据:除此之外,事务还能提高批量操作的效率,如在本地SQLit ...

  8. java开源时间/日期库Joda-Time

    任何企业应用程序都需要处理时间问题.应用程序需要知道当前的时间点和下一个时间点,有时它们还必须计算这两个时间点之间的路径.使用 JDK 完成这项任务将非常痛苦和繁琐.现在来看看 Joda Time,一 ...

  9. 快速学习JavaScript面向对象编程

    到处都是属性.方法,代码极其难懂,天哪,我的程序员,你究竟在做什么?仔细看看这篇指南,让我们一起写出优雅的面向对象的JavaScript代码吧! 作为一个开发者,能否写出优雅的代码对于你的职业生涯至关 ...

  10. ASP.NET MVC 学习之路由(URL Routing)

    在ASP.NET MVC中,一个URL请求是由对应的一个Controller中的Action来处理的,由URL Routing来告诉MVC如何定位到正确的Controller和Action. 默认路由 ...