zoukankan      html  css  js  c++  java
  • java高新技术-类加载器

    1.类加载器及委托机制的深入分析

       > 类加载器的作用:一个java文件中的出现的类,首先要把这个类的字节码加载到内存中,这个类的信息放在硬盘的classPath下的class文件中,

        把class文件中的内容加载到内存中去,在进行一些处理,处理完的结果就是字节码,这一系列工作是类加载器在做。

      > Java虚拟机中可以安装多个类加载器,系统默认三个主要类加载器,每个类负责加载特定位置的类:

          BootStrap,ExtClassLoader, AppClassLoader

      > 类加载器也是Java类,因为其他是java类的类加载器本身也要被类加载器加载,显然必须有第一个类加载器不是java类,

       这正是BootStrap

       BootStrap 很特殊,不是一个java类,是嵌套在jvm内核里的,jvm内核一启动BootStrap就已经在那里边了,BootStrap是由C++语言写的一段二进制代码,可以去加载别的类,包括别的类加载器

        System类是由BootStrap类加载器加载

     

    用eclipse 的打包工具将ClassLoaderTest 输出成jre/lib/ext 目录下的itcast.jar包,再在eclipse中运行这个类,运行结果显示为ExtClassLoader。此时的环境状态时classpath目录有ClassLoaderTest.class, ext/itcast.jar 包中也有ClassLoaderTest.class。

    当ExtClassLoader中能找到时,使用的是ExtClassLoader,当ExtClassLoader中找不到时,会去AppClassLoader中找,这里的原理是类加载器的委托机制。

    public static void main(String[] args) {
            System.out.println(ClassLoaderTest.class.getClassLoader().getClass()
                    .getName());  //sun.misc.Launcher$AppClassLoader
            
            /**
             * System 的类加载器为null 不代表没有类加载器,代表是由一个特殊的类加载器加载 BootStrap类加载器
             * 因为是嵌套在jvm里的,用java获取肯定获取不到,所有为null
             */
            System.out.println(System.class.getClassLoader()); //null
        
            /**
             * 以下程序打印
             * sun.misc.Launcher$AppClassLoader
                sun.misc.Launcher$ExtClassLoader
                null
             */
            ClassLoader loader = ClassLoaderTest.class.getClassLoader();
            while(loader != null){
                System.out.println(loader.getClass().getName());
                loader = loader.getParent();
            }
            System.out.println(loader);
        }

    sun.misc.Launcher$ExtClassLoader

    2.类加载器的委托机制

      2.1.可以定义类加载器,自定义类加载器需要继承ClassLoader,自定义的类加载器可以指定一个特定的目录

      2.2 当Java虚拟机要加载一个类时,到底派出哪个类加载器去加载呢?

        > 首先当前线程的类加载器去加载线程中的第一个类。getContextClassLoader() setContextClassLoader(ClassLoader cl)

        > 如果类A 中引用了类B ,Java虚拟机将使用加载类A的类加载器来加载类B。

        > 还可以直接调用ClassLoader.loadClass()方法来指定某个类加载器去加载某个类。

      2.3 每个类加载器加载类时,又先委托给其上级加载器。

        > 当所有祖宗类加载器没有加载到类,回到发起者类加载器,还加载不了,则抛ClassNotFoundException,

          不是再去找发起者类加载器的儿子,因为没有getChild方法,即使有,那有多少个儿子呢,找哪一个呢?

        > 对着类加载器的层次结构图和委托加载原理,解释先前将ClassLoaderTest输出成jar/lib/ext 目录下

          的itcast.jar包中后,运行结果为ExtClassLoader的原因

      2.4 模板方法设计模式

        父类 -->loadClass/findClass

          |----子类1(自己干)

          |----子类2(自己干)

        每个子类都有自己的方法逻辑,每个子类加载时都会去找其父节点,这个流程每个子类都会有,所以这个代码放在父类中

        我们要覆盖的是findClass,如果覆盖loadClass就会破坏这个流程

        得到class文件的转换成字节码 --> definedClass()

        

    protected Class<?> defineClass(String name, byte[] b, int off, int len)

      Api中的一段代码:

    The network class loader subclass must define the methods findClass and loadClassData to load a class from the network. Once it has downloaded the bytes that make up the class, it should use the method defineClass to create a class instance. A sample implementation is: 
    
         class NetworkClassLoader extends ClassLoader {
             String host;
             int port;
    
             public Class findClass(String name) {
                 byte[] b = loadClassData(name);
                 return defineClass(name, b, 0, b.length);
             }
    
             private byte[] loadClassData(String name) {
                 // load the class data from the connection
                  . . .
             }
         }

     编写对class文件进行加密的工具类

      

    public class MyclassLoader {
    
        public static void main(String[] args) throws Exception {
            String srcPath = args[0];
            String destDir = args[1];
            FileInputStream fis = new FileInputStream(srcPath);
            String destFileName = srcPath.substring(srcPath.lastIndexOf('\')+1);
            String destPath = destDir + "\" + destFileName;
            FileOutputStream fos = new FileOutputStream(destPath);
            cypher(fis, fos);
            fos.close();
            fis.close();
        }
        
        /**
         * 加密
         * @param is
         * @param ot
         * @throws IOException
         */
        private static void cypher(InputStream is,OutputStream ot) throws IOException{
            int b = -1;
            while((b = is.read()) != -1){
                ot.write(b ^ 0xff);  //异或 0变1 1变0
            }
        }
    }

     在工程目录下新建一个文件夹 itcastlib

    点击鼠标右键-> Run Configuration  填入运行参数

     运行后,在文件夹itcastlib中新增一个class文件

      有包名的类不能调用无包名的类

      使用正常的class文件能够正常打印

      System.out.println(new ClassLoaderAttachment().toString());

      使用加密后的class文件替换正常的没有加密的class文件,并打印toString()

      则会抛出异常

     必须用自定义的类加载器,对加密的class可以进行解密的操作

    private String classDir;
        
        @Override
        protected Class<?> findClass(String name) throws ClassNotFoundException {
            String classFileName = classDir + "\" + name + ".class";
            try {
                FileInputStream fis = new FileInputStream(classFileName);
                ByteArrayOutputStream bos = new ByteArrayOutputStream();
                cypher(fis, bos);
                fis.close();
                byte[] bytes = bos.toByteArray();
                return defineClass(bytes, 0, bytes.length);
            } catch (Exception e) {
            }
            return null;
        }
        
        public MyclassLoader(String classDir){
            this.classDir = classDir;
        }

    Class clazz = new MyclassLoader("itcastlib").loadClass("ClassLoaderAttachment");
    Date d1 = (Date)clazz.newInstance(); //ClassLoaderAttachment这个类不能显示出现,因为这个类还是乱的
    System.out.println(d1); //hello,itcast 解密成功

  • 相关阅读:
    【2012 百度之星资格赛 E:C++ 与Java】
    【hdu 1009】
    【2012 百度之星 / 初赛第一场 B:小小度刷礼品】
    【2012 百度之星资格赛 F:百科蝌蚪团】
    【在ubuntu下安装sublime text2】
    【2012 百度之星资格赛 B:小诺爱USB设备】
    【2012 百度之星资格赛 H:用户请求中的品牌】
    【pass】
    【黑客宣言】
    【基础训练题解 3106 石头剪刀布 】
  • 原文地址:https://www.cnblogs.com/wq3435/p/6006315.html
Copyright © 2011-2022 走看看