一、自定义类加载器的一般步骤

Java的类加载器自从JDK1.2开始便引入了一条机制叫做父类委托机制。一个类需要被加载的时候,JVM先会调用他的父类加载器进行加载,父类调用父类的父类,一直到顶级类加载器。如果父类加载器加载不了,依次再使用其子类进行加载。当然这类所说的父类加载器,不一定他们之间是继承的关系,有可能仅仅是包装的关系。

Java之所以出现这条机制,因为是处于安全性考虑。害怕用户自己定义class文件然后自己写一个类加载器来加载原本应该是JVM自己加载的类。这样会是JVM虚拟机混乱或者说会影响到用户的安全。下面我们来自己实现一个类加载器,其中主要就是继承ClassLoader类。我们有必要明白:

虽然在绝大多数情况下系统默认提供的类加载器实现已经可以满足需求。但是在某些情况下,您还是需要为应用开发出自己的类加载器。比如您的应用通过网络来传输 Java 类的字节代码,为了保证安全性,这些字节码经过了加密处理。这个时候您就需要自己的类加载器来从某个网络地址上读取加密后的字节代码,接着进行解密和验证,最后定义出要在 Java 虚拟机中运行的类来。下面将通过两个具体的实例来说明类加载器的开发。

①ClassLoader加载类的顺序

1调用findLoadedClass(String) 来检查是否已经加载类

2在父类加载器上调用loadClass方法。如果父亲不能加载,一次一级一级传给子类

3调用子类findClass(String) 方法查找类。若还加载不了就返回ClassNotFoundException,不交给发起请求的加载器的子加载器

②实现自己的类加载器

1 获取类的class文件的字节数组,如loadClassData方法

2 将字节数组转换为Class类的实例,重写findClass中调用的defineClass方法

  1. package cn.M_ClassLoader2;
  2. import java.io.ByteArrayOutputStream;
  3. import java.io.File;
  4. import java.io.FileInputStream;
  5. public class ClassLoaderTest
  6. {
  7. public static void main(String[] args) throws InstantiationException, IllegalAccessException,  ClassNotFoundException
  8. {
  9. // 新建一个类加载器
  10. MyClassLoader cl = new MyClassLoader("myClassLoader");
  11. // 加载类,得到Class对象
  12. Class<?> clazz = cl.loadClass("cn.M_ClassLoader2.Animal");
  13. // 得到类的实例
  14. Animal animal = (Animal) clazz.newInstance();
  15. animal.say();
  16. }
  17. }
  18. class Animal
  19. {
  20. public void say()
  21. {
  22. System.out.println("hello world!");
  23. }
  24. }
  25. class MyClassLoader extends ClassLoader
  26. {
  27. // 类加载器的名称
  28. private String name;
  29. // 类存放的路径
  30. private String path = MyClassLoader.getSystemClassLoader().getResource("").getPath();;
  31. MyClassLoader(String name)
  32. {
  33. this.name = name;
  34. }
  35. MyClassLoader(ClassLoader parent, String name)
  36. {
  37. super(parent);
  38. this.name = name;
  39. }
  40. /**
  41. * 重写findClass方法
  42. */
  43. @Override
  44. public Class<?> findClass(String name)
  45. {
  46. byte[] data = loadClassData(name);
  47. return this.defineClass(name, data, 0, data.length);
  48. }
  49. public byte[] loadClassData(String name)
  50. {
  51. try
  52. {
  53. name = name.replace(".", "//");
  54. FileInputStream is = new FileInputStream(new File(path + name + ".myclass"));
  55. ByteArrayOutputStream baos = new ByteArrayOutputStream();
  56. int b = 0;
  57. while ((b = is.read()) != -1)
  58. {
  59. baos.write(b);
  60. }
  61. System.out.println("我是自定义类加载器哦!");
  62. return baos.toByteArray();
  63. }
  64. catch (Exception e)
  65. {
  66. e.printStackTrace();
  67. }
  68. return null;
  69. }
  70. }

一般来说自己开发的类加载器只需要覆写findClass(String name)方法即可。java.lang.ClassLoader类的方法loadClass()封装了前面提到的代理模式的实现。该方法会首先调用findLoadedClass()方法来检查该类是否已经被加载过;如果没有加载过的话,会调用父类加载器的loadClass()方法来尝试加载该类;如果父类加载器无法加载该类的话,就调用findClass()方法来查找该类。因此,为了保证类加载器都正确实现代理模式,在开发自己的类加载器时,最好不要覆写loadClass()方法,而是覆写findClass()方法。

二、自定义类加载器的运行问题

由于只重写了findClass方法并没有重写loadClass方法,故没有改变父类委托机制。也就数说如果某个.class可以被父类加载,我们自定义的类加载器就不会被执行了。比如Animal.java被自动编译为Animal.class放在bin目录下,AppClassLoader完全可以加载,所以就不调用自定义的加载器了。

尝试办法1:把Animal.class放在别的目录中比如D盘的根目录下

报 Class A can not access a member of class B with modifiers ""错。Java语言中的包访问成员实际上指的是运行时包访问可见,而不是编译时。因此当你试图访问不在同一个runtime package的成员时,即便在编译时它们在同一个包内,但是却由不同的class loader加载,也同样会得到java.lang.IllegalAccessException: Class A can not access a member of class B with modifiers "" 这样的异常。

尝试办法2:把该Animal.class的后缀名为.myClass,让AppClassLoader找不到

网上有人说可以解决,但是我实验的结果是会和办法1报一样的异常。

尝试办法3:解决方案是通过扩展自定义的ClassLoader,重写loadClass方法先从当前类加载器加载再从父类加载器加载。

该解决办法是可以解决的,网址是http://blog.csdn.net/zhangxinrun/article/details/6161426

java类加载器学习2——自定义类加载器和父类委托机制带来的问题的更多相关文章

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

    Java虚拟机JVM学习06 自定义类加载器 父委托机制和命名空间的再讨论 创建用户自定义的类加载器 要创建用户自定义的类加载器,只需要扩展java.lang.ClassLoader类,然后覆盖它的f ...

  2. JAVA类加载器一 父类委托机制

    感谢原文作者:不将就! 原文链接:https://www.cnblogs.com/byron0918/p/5770653.html 类加载器负责将.class文件加载到内存中,并为之生成对应的Clas ...

  3. (转)《深入理解java虚拟机》学习笔记6——类加载机制

    Java虚拟机类加载过程是把Class类文件加载到内存,并对Class文件中的数据进行校验.转换解析和初始化,最终形成可以被虚拟机直接使用的java类型的过程. 在加载阶段,java虚拟机需要完成以下 ...

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

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

  5. 《深入理解Java虚拟机》学习笔记之类加载

    之前在学习ASM时做了一篇笔记<Java字节码操纵框架ASM小试>,笔记里对类文件结构做了简介,这里我们来回顾一下. Class类文件结构 在Java发展之初设计者们发布规范文档时就刻意把 ...

  6. Struts2重新学习之自定义拦截器(判断用户是否是登录状态)

    拦截器 一:1:概念:Interceptor拦截器类似于我们学习过的过滤器,是可以再action执行前后执行的代码.是web开发时,常用的技术.比如,权限控制,日志记录. 2:多个拦截器Interce ...

  7. Struts学习之自定义拦截器

    * 所有的拦截器都需要实现Interceptor接口或者继承Interceptor接口的扩展实现类    * 要重写init().intercept().destroy()方法        * in ...

  8. java8学习之自定义收集器深度剖析与并行流陷阱

    自定义收集器深度剖析: 在上次[http://www.cnblogs.com/webor2006/p/8342427.html]中咱们自定义了一个收集器,这对如何使用收集器Collector是极有帮助 ...

  9. java8学习之自定义收集器实现

    在上次花了几个篇幅对Collector收集器的javadoc进行了详细的解读,其涉及到的文章有: http://www.cnblogs.com/webor2006/p/8311074.html htt ...

随机推荐

  1. Xcode常用设置

    1.设置主题和字体大小 2.设置显示代码行号

  2. Objective-C程序结构及语法特点

    程序文件分为头文件(.h)和实现文件(.m): 使用#import关键字将所需的头文件导入程序,并且可以避免程序重复引用相同的头文件: @autoreleasepool { … } 自动释放池: 符号 ...

  3. 【HDU2222】Keywords Search

    Problem DescriptionIn the modern time, Search engine came into the life of everybody like Google, Ba ...

  4. Experience all that SharePoint 15 has to offer. Start now or Remind me later.

    $spSite = Get-SpSite($waUrl); $spSite.AllowSelfServiceUpgrade = $false

  5. poj 3041 Asteroids (最大匹配最小顶点覆盖——匈牙利模板题)

    http://poj.org/problem?id=3041 Asteroids Time Limit: 1000MS   Memory Limit: 65536K Total Submissions ...

  6. hadoop Safe mode is ON 的解决办法

    hadoop Safe mode is ON 的解决办法 搭了一个hadoop集群环境,近期总是出现读写文件错误的情况,查看name node的日志显示 (Safe mode is ON) Safe ...

  7. uva 1377

    比较不错的一个题,关键是理解状态转移 #include<algorithm> #include<cstdio> #include<cstring> #include ...

  8. Android 调用系统的拍相程序进行录像

    xml: <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android ...

  9. linux更改shell

    1.查看机器安装了哪些shell? 有两种方法可以查看. 第一种: 使用env命令查看环境变量里面的shell信息第二种: $ cat /etc/shells 2.查看当前正在使用的shell是哪个? ...

  10. ubuntu12 开机自动转到命令行

    命令: sudo gedit /etc/default/grub 找到这一行 GRUB_CMDLINE_LINUX_DEFAULT="quiet splash"改成 GRUB_CM ...