类加载器
2016-01-27 19:33
381 查看
一.三大类加载
Java虚拟机中可以安装多个类加载器,系统默认三个主要的类加载器是:BootStrap、ExtClassLoard、AppClassLoard,其中BottStrap不是java类,他同虚拟器一样是跨平台的,也就是随着操作系统的不同而改变,他是第一个进行内存开辟的类加载器,就是他把其他两个类加载器加载到虚拟机当中的,而其他两个都是java类,这三个类加载器都负责加载特定位置上的类。
二.类加载器之间的关系
类加载器之间是一个继承树的关系,如果我们需要自定义类加载器就要让他成为这个体系中的一员。下面把每个类加载器与其对应的需加载的类进行说明:
三.委托机制
如果当前线程的类加载器去加载线程中的第一个类A,当类A引用了类B时,java虚拟机将使用类A的类加载器来加载类B,除去这种默认方法,我们还可以通过直接调用ClassLoader.loadClass()方法来指定类加载器。
当我们要使用到一个类时,首先当前的类加载器会委托上级加载器先去加载该类,当上级收到请求后不是直接去他指定的目录下寻找该类,而是再去找上级,一直到最顶层,这时最顶层的类加载器不能再往上抛了,他就去他的指定目录下寻找该类,如果没有找到,就返回给他的下一级,以此类推,最后返回到发起者手中,如果发起者都没有加载到该类,就会报出类不存在异常:ClassNoFoundExeption。
委托机制的好处在于当上级加载器已经把某Class文件加载到内存当中时,如果再调用该类,就无需再次加载,这就避免了多份字节码同时存在的情况发生。
四.自定义类加载器
自定义类加载器总共需要三步
①自定义类加载器首先要有一个前提,就是继承ClassLoard类(抽象类),这样我们的自定义类才能成为类加载器体系中的一员
②然后就需要复写ClassLoard中的方法,在自定义类加载器时一般都会复写findClass(String name)方法,用于当上级加载器找不到所需类时,自己去加载该类,还有一个方法比较重要:loardClass(String
name),此方法大多数情况不用复写,他也是用来根据类名获取该类字节码,他不用复写的原因在于这个方法会使用委托机制,当上级没找到返回来之后,此方法会启用findClass方法,所以说我们要想保留委托机制,就不要去复写该方法,而只改动findClass方法就可以。
③使用ClassLoard中的defineClass()方法将找到的Class文件加载到内存当中转换成字节码文件。
五.练习
编写一个程序,首先自定义一个类,然后自定义类加载器加载该类,我们希望生成的Class文件是经过加密的,而我们自定义的类加载器可以具有解密的功能。
首先自定义一个类,这个类需要继承一个可以被编译器加载的类,因为在后期使用时,我们不能使用这个类名,那个时候我们的自定义类加载器还没有加载该类。
自定义类加载器,在该加载器中定义加密和解密的方法,为了方便使用异或,这样该方法既可以加密又可以解密。在main函数中,我们在运行时期动态的把原Class文件的绝对路径和我们自定义类指定的目录(此处为classLoard)作为参数传递给main函数,因为Eclipse会实时编译把ClassLoadAcce的Class文件存放到bin目录下,我们利用IO流和加密方法读取bin目录下的class文件,加密后把他放到自定义目录下。复写findClass方法,加载加密后的class文件,并进行解密。
在该类中使用自定义类加载器加载该文件
Java虚拟机中可以安装多个类加载器,系统默认三个主要的类加载器是:BootStrap、ExtClassLoard、AppClassLoard,其中BottStrap不是java类,他同虚拟器一样是跨平台的,也就是随着操作系统的不同而改变,他是第一个进行内存开辟的类加载器,就是他把其他两个类加载器加载到虚拟机当中的,而其他两个都是java类,这三个类加载器都负责加载特定位置上的类。
二.类加载器之间的关系
类加载器之间是一个继承树的关系,如果我们需要自定义类加载器就要让他成为这个体系中的一员。下面把每个类加载器与其对应的需加载的类进行说明:
三.委托机制
如果当前线程的类加载器去加载线程中的第一个类A,当类A引用了类B时,java虚拟机将使用类A的类加载器来加载类B,除去这种默认方法,我们还可以通过直接调用ClassLoader.loadClass()方法来指定类加载器。
当我们要使用到一个类时,首先当前的类加载器会委托上级加载器先去加载该类,当上级收到请求后不是直接去他指定的目录下寻找该类,而是再去找上级,一直到最顶层,这时最顶层的类加载器不能再往上抛了,他就去他的指定目录下寻找该类,如果没有找到,就返回给他的下一级,以此类推,最后返回到发起者手中,如果发起者都没有加载到该类,就会报出类不存在异常:ClassNoFoundExeption。
委托机制的好处在于当上级加载器已经把某Class文件加载到内存当中时,如果再调用该类,就无需再次加载,这就避免了多份字节码同时存在的情况发生。
四.自定义类加载器
自定义类加载器总共需要三步
①自定义类加载器首先要有一个前提,就是继承ClassLoard类(抽象类),这样我们的自定义类才能成为类加载器体系中的一员
②然后就需要复写ClassLoard中的方法,在自定义类加载器时一般都会复写findClass(String name)方法,用于当上级加载器找不到所需类时,自己去加载该类,还有一个方法比较重要:loardClass(String
name),此方法大多数情况不用复写,他也是用来根据类名获取该类字节码,他不用复写的原因在于这个方法会使用委托机制,当上级没找到返回来之后,此方法会启用findClass方法,所以说我们要想保留委托机制,就不要去复写该方法,而只改动findClass方法就可以。
③使用ClassLoard中的defineClass()方法将找到的Class文件加载到内存当中转换成字节码文件。
五.练习
编写一个程序,首先自定义一个类,然后自定义类加载器加载该类,我们希望生成的Class文件是经过加密的,而我们自定义的类加载器可以具有解密的功能。
首先自定义一个类,这个类需要继承一个可以被编译器加载的类,因为在后期使用时,我们不能使用这个类名,那个时候我们的自定义类加载器还没有加载该类。
//这个类就是要被我们自定义类加载器加载的类 public class ClassLoadAcce extends Date{ public String toString() { return "你好!自定义类加载器!"; } }
自定义类加载器,在该加载器中定义加密和解密的方法,为了方便使用异或,这样该方法既可以加密又可以解密。在main函数中,我们在运行时期动态的把原Class文件的绝对路径和我们自定义类指定的目录(此处为classLoard)作为参数传递给main函数,因为Eclipse会实时编译把ClassLoadAcce的Class文件存放到bin目录下,我们利用IO流和加密方法读取bin目录下的class文件,加密后把他放到自定义目录下。复写findClass方法,加载加密后的class文件,并进行解密。
/* * 这是我们自己定义的一个类加载器 * ①首先定义一个附件类用来产生Class文件,这个类叫做ClassLoadAcce,他生成的Class文件是放在 * bin目录下,我们自定义一个目录放在该工程下名字叫做classLoard。 * * ②经过args(main方法参数)读取ClassLoadAcce.class的路径名然后经过IO流的加密功能把加密过的class文件放到 * 自定义的目录classLoad下面 * * ③复写ClassLoad类的findClass方法,当委托机制找不到该class文件时,返回到发起者手中 * 也就是我们的自定义加载器这里,他就会执行findClass方法来 拯救世界了 * findClass方法中从指定的文件中读取出Class文件,并返回给调用者 * * */ public class MyClassLoard extends ClassLoader{ public static void main(String[] args) throws IOException{ //从javac中接收两个参数,分别是源文件和目标目录 String source = args[0]; String destPath = args[1]; //构建文件读取流,获取该文件 FileInputStream fis = new FileInputStream(source); System.out.println(source); //从源文件中截取文件名 String fileName = source.substring(source.lastIndexOf("\\")+1); //将截取好的文件名写入到当前项目中 String fileDest = destPath+File.separator+fileName; FileOutputStream fos = new FileOutputStream(fileDest); encrypt(fis,fos); fis.close() 4000 ; fos.close(); } //定义一个简单的加密算法,使用异或 private static void encrypt(InputStream is,OutputStream os)throws IOException { int len=-1; while((len = is.read())!=-1) { //异或255进行加密 os.write(len^255); os.flush(); } } //定义成员变量,用来接收指定的class路径 private String classPath; public MyClassLoard(){} public MyClassLoard(String classPath) { this.classPath = classPath; } //复写findClass方法让他去指定目录classLoad中寻找class类 @Override protected Class<?> findClass(String name) throws ClassNotFoundException { //根据使用自定义类加载器时传进来的文件目录classPath和文件名name,获取该class的全部名称 StringBuilder className = new StringBuilder(classPath); className.append(File.separator+name+".class"); System.out.println("自定义类加载器所要获取的文件路径:"+className); //读取该文件 try { FileInputStream fis = new FileInputStream(className.toString()); //把读取到的class文件写到内存中 ByteArrayOutputStream bao = new ByteArrayOutputStream(); //对读取到文件进行解密 encrypt(fis, bao); fis.close(); byte[] buf = bao.toByteArray(); //返回读取到的class文件 return defineClass(buf, 0, buf.length); }catch(IOException e) { e.printStackTrace(); } //否则返回委托机制找父类去 return super.findClass(name); } }
在该类中使用自定义类加载器加载该文件
public class ClassLoadTest { public static void main(String[] args) throws Exception{ /*获取我们要读取的那个Class文件字节码,此处类加载器的发起者是AppClassLoader, 如果我们已经把bin目录下的class文件删掉了,此处报错,如果没删,此处读取的是bin目录下的class文件*/ // System.out.println(new ClassLoadAcce().toString()); // System.out.println(new ClassLoadAcce().getClass().getClassLoader()); /*使用我们自定义的类加载器,他会先使用委托机制向上找,找不到返回到发起者这里来 我们自定义findClass方法在classLoad文件夹下找到加密过得class文件并返回*/ Class cls = new MyClassLoard("classLoad").loadClass("ClassLoadAcce"); System.out.println(cls); //此处不能直接new ClassLoadAcce对象,因为ClassLoadAcce在编译时期还没有吧他的字节码加载到内存中 Date ca= (Date)cls.newInstance(); System.out.println(ca); } }
相关文章推荐
- Educational Codeforces Round 6 B
- spark-shell的环境测试
- effective C++ 笔记二
- 让多个Fragment 切换时不重新实例化
- SPOJ Distinct Substrings 后缀数组
- linux gdb 没有符号表被读取。请使用 "file" 命令。
- 为了听技术干货,小伙伴们也够拼的!
- Objective-C 单例
- Win32使用剪切区域实现酷狗动态歌词特效:动态歌词颜色滚动覆盖效果
- Git的使用—分支操作
- 针对补考科目的学习计划
- 20160127
- spark的slave上没有datanode 的解决办法
- hdu3709
- 使用Maven打包项目并上传到Linux服务器
- ES6个人学习整理(六)——Promise
- 建立学习型组织 - 解决了我几年的困惑 - IT十年 - 博客频道 - CSDN.NET
- 远程数据管理端开关
- Nth to Last Node in List
- Android工具与其它