黑马程序员—类加载器

来源:互联网 发布:五大流氓 知乎 编辑:程序博客网 时间:2024/04/29 09:47

------- android培训、java培训、期待与您交流! ----------

 

1、类加载器

 

1、定义:简单说,类加载器就是加载类的工具。

当出现一个类,用到此类的时候,Java虚拟机首先将类字节码加载进内存,通常字节码的原始信息放在硬盘上的classpath指定的目录下。

2、类加载器作用:将.class文件中的内容加载进内存进行处理,处理完后的结果就是字节码。

3、默认类加载器:

1)Java虚拟机中可安装多个类加载器,系统默认的有三个主要的,每个类负责加载特定位置的类:BootStrap、ExtClassLoader、AppClassLoader

2)BootStrap--顶级类加载器:

 

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

1.首先当前线程的类加载器去加载线程中的第一个类。

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

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

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

1.当所有祖宗类加载器没有加载到类,回到发起者类加载器,还加载不了,则抛ClassNotFoundException,不是再去找发起者类加载器的儿子,因为没有getChild方法,即使有,那有多个儿子,找哪一个呢?

2.对着类加载器的层次结构图和委托加载原理,解释先前将ClassLoaderTest输出成jre/lib/ext目录下的itcast.jar包中后,运行结果为ExtClassLoader的原因。

 

每个ClassLoader本身只能分别加载特定位置和目录中的类,但它们可以委托其他的类装载器去加载类,这就是类加载器的委托模式。类装载器一级级委托到BootStrap类加载器,当BootStrap无法加载当前所要加载的类时,然后才一级级回退到子孙类装载器去进行真正的加载。当回退到最初的类装载器时,如果它自己也不能完成类的装载,那就应报告ClassNotFoundException异常。

 

2、类加载器的委托机制

 

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

1.首先当前线程的类加载器去加载线程中的第一个类。

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

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

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

1.当所有祖宗类加载器没有加载到类,回到发起者类加载器,还加载不了,则抛ClassNotFoundException,不是再去找发起者类加载器的儿子,因为没有getChild方法,即使有,那有多个儿子,找哪一个呢?

2.对着类加载器的层次结构图和委托加载原理,解释先前将ClassLoaderTest输出成jre/lib/ext目录下的itcast.jar包中后,运行结果为ExtClassLoader的原因。

 

每个ClassLoader本身只能分别加载特定位置和目录中的类,但它们可以委托其他的类装载器去加载类,这就是类加载器的委托模式。类装载器一级级委托到BootStrap类加载器,当BootStrap无法加载当前所要加载的类时,然后才一级级回退到子孙类装载器去进行真正的加载。当回退到最初的类装载器时,如果它自己也不能完成类的装载,那就应报告ClassNotFoundException异常。

 

3、一个类加载器的高级问题分析

 

编写一个能打印出自己的类加载器名称和当前类加载器的父子结构关系链的MyServlet,正常发布后,看到打印结果为WebAppClassloader。

把MyServlet.class文件打jar包,放到ext目录中,重启tomcat,发现找不到HttpServlet的错误。

把servlet.jar也放到ext目录中,问题解决了,打印的结果是ExtclassLoader 。

父级类加载器加载的类无法引用只能被子级类加载器加载的类,原理如下图:

 

 

4、编写自己的类加载器

 

知识讲解:

Ø 自定义的类加载器的必须继承ClassLoader

Ø loadClass方法与findClass方法

Ø defineClass方法

编程步骤:

Ø 编写一个对文件内容进行简单加密的程序。

Ø 编写了一个自己的类装载器,可实现对加密过的类进行装载和解密。

Ø 编写一个程序调用类加载器加载类,在源程序中不能用该类名定义引用变量,因为编译器无法识别这个类。程序中可以除了使用ClassLoader.load方法之外,还可以使用设置线程的上下文类加载器或者系统类加载器,然后再使用Class.forName。

实验步骤:

Ø 对不带包名的class文件进行加密,加密结果存放到另外一个目录,例如:java MyClassLoader MyTest.class F:\itcast

Ø 运行加载类的程序,结果能够被正常加载,但打印出来的类装载器名称为AppClassLoader:java MyClassLoader MyTestF:\itcast

Ø 用加密后的类文件替换CLASSPATH环境下的类文件,再执行上一步操作就出问题了,错误说明是AppClassLoader类装载器装载失败。

Ø 删除CLASSPATH环境下的类文件,再执行上一步操作就没问题了。

 

public static voidmain(String[] args) throws Exception {        StringsrcPath = args[0];        StringdestDir = args[1];        FileInputStreamfis = new FileInputStream(srcPath);        StringdestFileName = srcPath.substring(srcPath.lastIndexOf('\\')+1);        StringdestPath = destDir + "\\" + destFileName;        FileOutputStreamfos = new FileOutputStream(destPath);        cypher(fis,fos);        fis.close();        fos.close();    }   // 加密方法,同时也是解密方法    privatestatic void cypher(InputStream ips ,OutputStream ops) throws Exception{        intb = -1;        while((b=ips.read())!=-1){            ops.write(b^ 0xff);//如果是1就变成0,如果是0就变成1        }    }


然后在新建一个类,通过上面的方法将新建的类的字节码进行加密:

public classClassLoaderAttachment extends Date {

    publicString toString(){

        return"hello,itcast";

    } 

}

并在工程里新建一个文件夹,用来保存加密后的class文件.

 

那么这就需要使用我们自己的类加载器来进行解密了.

public classMyClassLoader extends ClassLoader{     publicstatic void main(String[] args) throws Exception {        StringsrcPath = args[0];        StringdestDir = args[1];        FileInputStreamfis = new FileInputStream(srcPath);        StringdestFileName = srcPath.substring(srcPath.lastIndexOf('\\')+1);        StringdestPath = destDir + "\\" + destFileName;        FileOutputStreamfos = new FileOutputStream(destPath);        cypher(fis,fos);        fis.close();        fos.close();    } 


    /**

    * 加密方法,同时也是解密方法

    * @param ips

    * @param ops

    * @throws Exception

    */

    

privatestatic void cypher(InputStream ips ,OutputStream ops) throws Exception{        intb = -1;        while((b=ips.read())!=-1){            ops.write(b^ 0xff);//如果是1就变成0,如果是0就变成1        }    }     privateString classDir;     @Override    protectedClass<?> findClass(String name) throws ClassNotFoundException {        StringclassFileName = classDir + "\\" +name.substring(name.lastIndexOf('.')+1) + ".class";        try{            FileInputStreamfis = new FileInputStream(classFileName);            ByteArrayOutputStreambos = new ByteArrayOutputStream();            cypher(fis,bos);            fis.close();            System.out.println("aaa");            byte[]bytes = bos.toByteArray();            returndefineClass(bytes, 0, bytes.length);        }catch (Exception e) {            e.printStackTrace();        }        returnnull;    }     publicMyClassLoader(){    }     publicMyClassLoader(String classDir){        this.classDir= classDir;    }}

测试运行代码:

 

      

  Classclazz = new MyClassLoader("myClass").loadClass("ClassLoaderAttachment");        //此处不能在使用ClassLoaderAttachment因为一旦用了之后,        //系统的类加载器就会去加载,导致失败,所以该类就继承了Date类了.        Datedate = (Date)clazz.newInstance();       System.out.println(date);

 

 

 

 

 

------- android培训、java培训、期待与您交流! ----------详细请查看:http://edu.csdn.net

 

0 0
原创粉丝点击