A股上市公司传智教育(股票代码 003032)旗下技术交流社区北京昌平校区

 找回密码
 加入黑马

QQ登录

只需一步,快速开始

© IT_JM 中级黑马   /  2013-10-14 16:05  /  1817 人查看  /  1 人回复  /   0 人收藏 转载请遵从CC协议 禁止商业使用本文

  1. 1. package cn.itheima.demo;
  2. 2.
  3. 3. public class ClassLoaderDemo {
  4. 4.
  5. 5. public static void main(String[] args) {
  6. 6. System.out.println(
  7. 7. ClassLoaderDemo.class.getClassLoader().getClass().getName()
  8. 8. );//sun.misc.Launcher$$$$AppClassLoader,表示由AppClassLoader加载
  9. 9. System.out.println(System.class.getClassLoader());//null,表示System这个类时由RootStrap加载的
  10. 10. }
  11. 11. }
复制代码
二、类加载器的委托机制
1、每个ClassLoader本身只能分别加载特定位置和目录中的类,但它们可以委托其他的类加载器去加载类,这就是类加载器的委托模式。
2、加载类的方式
        当Java虚拟机要加载一个类时,到底要用哪个类加载器加载呢?
         1)首先,当前线程的类加载器去加载线程中的第一个类。
         2)若A引用类B(继承或者使用了B),Java虚拟机将使用加载类的类加载器来加载类B。
         3)还可直接调用ClassLoader的LoaderClass()方法,来指定某个类加载器去加载某个类。
2、每个类加载器加载类时,又先委托给上级类加载器。
        类装载器一级级委托到BootStrap类加载器,当BootStrap无法加载当前所要加载的类时,然后才一级级回退到子孙类加载器去进行加载。当回退到最初的发起者类装载器时,如果它自己也不能完成类的装载,那就会抛出ClassNotFoundException异常。这时就不会再委托发起者加载器的子类去加载了,如果它还有子类的话。
        简单说,就是先由发起者将类一级级委托为BootStrap,从父级开始找,找到了直接返回,没找到再助剂让其子级找,直到发起者,再没找到就报异常。
3、委托机制的优点:可以集中管理,不会产生多字节码重复的现象。
补充:面试题
        可不可以自己写个类为:java.lang.System呢?
        回答:第一、通常是不可以的,由于类加载器的委托机制,会先将System这个类一级级委托给最顶级的BootStrap,由于BootStrap在其指定的目录中加载的是rt.jar中的类,且其中有System这个类,那么就会直接加载自己目录中的,也就是Java已经定义好的System这个类,而不会加载自定义的这个System。
        第二、但是还是有办法加载这个自定义的System类的,此时就不能交给上级加载了,需要用自定义的类加载器加载,这就需要有特殊的写法才能去加载这个自定义的System类的。
体现委托机制的示例:
  1. 1. package cn.itheima.demo;
  2. 2.
  3. 3. public class ClassLoaderDemo {
  4. 4.
  5. 5. public static void main(String[] args) {
  6. 6. /*
  7. 7. * 用eclipse的打包工具将ClassLoaderTest输出成jre/lib/ext目录下的itheima.jar包
  8. 8. * 此时再在eclipse中运行这个类时,下面代码的while循环内的运行结果显示为ExtClassLoadr。
  9. 9. * 这就表示,AppClassLoader在加载这个类时,会先委托给其上一级ExtClassLoader加载器去加载,而上级又委托上级
  10. 10. * 但是ExtClassloader的上级没有找到要加载的类,就回到ExtClassLoader,此时它在jre/lib/ext中找到了,所以就结果就显示它了。
  11. 11. * */
  12. 12. ClassLoader loader=ClassLoaderDemo.class.getClassLoader();
  13. 13. while (loader!=null) {
  14. 14. System.out.println(loader.getClass().getName());
  15. 15. loader=loader.getParent();//将此loader的上级赋给loader
  16. 16. }
  17. 17. System.out.println(loader);
  18. 18. }
  19. 19. }
复制代码
三、自定义类加载器
1、自定义的类加载器必须继承抽象类ClassLoader,要覆写其中的findClass(String name)方法,而不用覆写loadClass()方法。
2、覆写findClass(Stringname)方法的原因:
        1)在loadClass()内部是会先委托给父级,当父级找不到后返回,再调用findClass(String name)方法,也就是你自定义的类加载器去找。所以只需要覆写findClass方法,就能实现用自定义的类加载器加载类的目的。
        因为,一般自定义类加载器,会把需要加载的类放在自己指定的目录中,而java中已有的类加载器是不知道你这个目录的,所以会找不到。这样才会调用你复写的findClass()方法,用你自定义的类加载器去指定的目录加载类。
        2)这是一种模板方法设计模式。这样就保留了loadClass()方法中的流程(这个流程就是先找父级,找不到再调用自定义的类加载器),而我们只需复写findClass方法,实现局部细节就行了。
        ClassLoader提供了一个protected  Class<?>defineClass(String name, byte[] b, int off, int len)方法,只需要将类对应的class文件传入,就可以将其变为字节码。
3、编程步骤:
        1)编写一个对文件内容进行简单加密的程序
        2)编写好了一个自己的类加载器,可实现对加密过来的类进行加载和解密。
        3)编写一个程序,调用类加载器加载类,在源程序中不能用该类名定义引用变量,因为编译器无法识别这个类,程序中除了可使用ClassLoader的loadClass方法外,还可以使用设置线程的上下文类加载器或系统类加载器,然后再使用Class.forName。
4、编码步骤:
        1)对不带包名的class文件进行加密,加密结果存放到另外一个目录,例如: java MyClassLoader MyTest.class F:\itcast
        2)运行加载类的程序,结果能够被正常加载,但打印出来的类装载器名称为AppClassLoader:java MyClassLoader MyTest F:\itcast
        3)用加密后的类文件替换CLASSPATH环境下的类文件,再执行上一步操作就出问题了,错误说明是AppClassLoader类装载器装载失败。
        4)删除CLASSPATH环境下的类文件,再执行上一步操作就没问题了。
  1. 1. package cn.itheima.demo;
  2. 2.
  3. 3. import java.util.Date;
  4. 4. //定义一个测试类,继承Date,便于使用时加载
  5. 5. public class ClassLoaderAttachment extends Date{
  6. 6. //复写toString方法
  7. 7. public String toString(){
  8. 8. return "Hello World!";
  9. 9. }
  10. 10. }
  11. 11.
  12. 12. import java.io.ByteArrayOutputStream;
  13. 13. import java.io.FileInputStream;
  14. 14. import java.io.FileOutputStream;
  15. 15. import java.io.InputStream;
  16. 16. import java.io.OutputStream;
  17. 17.
  18. 18. public class MyClassLoader extends ClassLoader{
  19. 19.
  20. 20. public static void main(String[] args) throws Exception {
  21. 21. String srcPath=args[0];//文件源
  22. 22. String destDir=args[1];//文件目的
  23. 23. InputStream ips=new FileInputStream(srcPath);
  24. 24. String destFileName=srcPath.substring(srcPath.lastIndexOf("\\")+1);
  25. 25. String destFilePath=destDir+"\\"+destFileName;
  26. 26. OutputStream ops=new FileOutputStream(destFilePath);
  27. 27. cypher(ips,ops);//加密class字节码
  28. 28. ips.close();
  29. 29. ops.close();
  30. 30. }
  31. 31. //加密方法
  32. 32. private static void cypher(InputStream ips,OutputStream ops) throws Exception{
  33. 33. int b=-1;
  34. 34. while((b=ips.read())!=-1){
  35. 35. ops.write(b^0xff);
  36. 36. }
  37. 37. }
  38. 38.
  39. 39. @Override
  40. 40. //覆盖ClassLoader的findClass方法
  41. 41. protected Class<?> findClass(String name) throws ClassNotFoundException {
  42. 42. name=name.substring(name.lastIndexOf(".")+1);
  43. 43. String classFileName=classDir+"\\"+name+".class";//获取class文件名
  44. 44. InputStream ips=null;
  45. 45. try {
  46. 46. ips=new FileInputStream(classFileName);
  47. 47. ByteArrayOutputStream bos=new ByteArrayOutputStream();//定义字节数组流
  48. 48. cypher(ips,bos);//解密
  49. 49. ips.close();
  50. 50. byte[] buf=bos.toByteArray();//取出字节数组流中的数据
  51. 51. return defineClass(null, buf,0,buf.length);//加载进内存
  52. 52.
  53. 53. } catch (Exception e) {
  54. 54. // TODO: handle exception
  55. 55. e.printStackTrace();
  56. 56. }
  57. 57. return null;
  58. 58. //return super.findClass(name);
  59. 59. }
  60. 60.
  61. 61. private String classDir;
  62. 62. public MyClassLoader(){}
  63. 63. //带参数的构造函数
  64. 64. public MyClassLoader(String classDir){
  65. 65. this.classDir=classDir;
  66. 66. }
  67. 67.
  68. 68. }
  69. 69.
  70. 70. import java.util.Date;
  71. 71.
  72. 72. public class ClassLoaderDemo {
  73. 73.
  74. 74. public static void main(String[] args) throws Exception {
  75. 75. //将用自定义的类加载器加载.class文件
  76. 76. Class clazz=new MyClassLoader("itheimalib").loadClass("cn.itheima.demo.ClassLoaderAttachment");
  77. 77. Date d1 = (Date)clazz.newInstance();//获取Class类的实例对象
  78. 78. System.out.println(d1);
  79. 79. }
  80. 80. }
复制代码

评分

参与人数 1技术分 +1 收起 理由
黄文伯 + 1 神马都是浮云

查看全部评分

1 个回复

倒序浏览
{:soso_e129:}前排
回复 使用道具 举报
您需要登录后才可以回帖 登录 | 加入黑马