---------------------- <a target="blank">ASP.Net+Android+IO开发S</a>、<a target="blank">.Net培训</a>、期待与您交流! ----------------------
类加载器
简要介绍什么是类加载器和类加载器的作用:
Java虚拟机中可以安装多个类加载器,系统默认三个主要类加载器,每个类负责加载特定位置
类:BootStrap,ExtClassLoader,AppClassLoader。
类加载器也是Java类,因为其他是java类的类加载器本身也要被类加载器加载,显然必须有第一个类加载器不是不是java类,这正是
BootStrap。
Java虚拟机中的所有类装载器采用具有父子关系的树形结构进行组织,在实例化每个类装载器对象时,需要为其指定一个父级类装载器对象
或者默认采用系统类装载器为其父级类加载。
类加载器的委托机制
当Java虚拟机要加载一个类时,到底派出哪个类加载器去加载呢?
首先当前线程的类加载器去加载线程中的第一个类。
如果类A中引用了类B,Java虚拟机将使用加载类A的类装载器来加载类B。
还可以直接调用ClassLoader.loadClass()方法来指定某个类加载器去加载某个类。
每个类加载器加载类时,又先委托给其上级类加载器。
当所有祖宗类加载器没有加载到类,回到发起者类加载器,还加载不了,则抛ClassNotFoundException,不是再去找发起者类加载器
的儿子,因为没有getChild方法,即使有,那有多个儿子,找哪一个呢?
对着类加载器的层次结构图和委托加载原理,解释先前将ClassLoaderTest输出成jre/lib/ext目录下的itcast.jar包中后,运行结果为
ExtClassLoader的原因。
编写自己的类加载器
知识讲解:
自定义的类加载器的必须继承ClassLoader
loadClass方法与findClass方法、defineClass方法
编程步骤:
编写一个对文件内容进行简单加密的程序。
编写了一个自己的类装载器,可实现对加密过的类进行装载和解密。
编写一个程序调用类加载器加载类,在源程序中不能用该类名定义引用变量,因为编译器无法识别这个类。程序中可以除了使用
ClassLoader.load方法之外,还可以使用设置线程的上下文类加载器或者系统类加载器,然后再使用Class.forName。
实验步骤:
对不带包名的class文件进行加密,加密结果存放到另外一个目录,例如: java MyClassLoader MyTest.class E:\itcast
运行加载类的程序,结果能够被正常加载,但打印出来的类装载器名称为AppClassLoader:java MyClassLoader MyTest E:\itcast
用加密后的类文件替换CLASSPATH环境下的类文件,再执行上一步操作就出问题了,错误说明是AppClassLoader类装载器装载失败。
删除CLASSPATH环境下的类文件,再执行上一步操作就没问题了。
一个类加载器的高级问题分析:
编写一个能打印出自己的类加载器名称和当前类加载器的父子结构关系链的MyServlet,正常发布后,看到打印结果:
WebAppClassloader。
把MyServlet.class文件打jar包,放到ext目录中,重启tomcat,发现找不到HttpServlet的错误。
把servlet.jar也放到ext目录中,问题解决了,打印的结果是ExtclassLoader 。
父级类加载器加载的类无法引用只能被子级类加载器加载的类
- <span style="font-family:Arial;">package cn.itcast.text2;
- import java.util.Date;
- public class ClassLoaderAttachment extends Date {
- //对此类进行加密
- public String toString(){
- return "hello world";
- }
- public static void main(String [] args){
- }
- }
- //自定义类加载器
- package cn.itcast.text2;
- import java.io.*;
- //继承抽象类ClassLoader
- public class MyClassLoader extends ClassLoader {
- public static void main(String[] args) throws Exception {
- //传入两个参数,源和目标
- String scrPath = args[0];
- String destDir = args[1];
- //将数据读取到输入流中,并写入到输出流中
- FileInputStream fis = new FileInputStream(scrPath);
- String destFileName =
- scrPath.substring(scrPath.lastIndexOf('\\')+1);
- String destPath = destDir + "\\" + destFileName;
- FileOutputStream fos = new FileOutputStream(destPath);
- //加密数据
- cypher(fis,fos);
- fis.close();
- fos.close();
- }
- //定义加密数据的方法
- private static void cypher(InputStream ips,OutputStream ops)throws Exception{
- int b = 0;
- while((b=ips.read())!=-1){
- ops.write(b ^ 0xff);
- }
- }
- //定义全局变量
- private String classDir;
- @Override//覆写findClass方法,自定义类加载器
- 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) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- //如果没找到类,则用父级类加载器加载
- return super.findClass(name);
- }
- //构造函数
- public MyClassLoader(){}
- public MyClassLoader(String classDir){
- this.classDir = classDir;
- }
- }
- </span>