黑马程序员-高新技术-类加载器和委托

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

44    类加载器及其委托机制的深入分析

1.类加载器

 Java虚拟机中可以安装多个类加载器,系统默认三个主要类加载器(BootStrap,ExtClassLoader,AppClassLoader),每个类加载器负责加载特定位置的类.除了BootStrap以外的类加载器都是Java类,要被其他类加载器加载,最终的类加载器就是bootStrap.Java虚拟机中的所有类加载器采用具有父子关系的树形结构进行组织,在实例化每个类加载器对象时,需要为其指定一个父级类加载器对象或者默认采用系统类加载器为其父级类加载器.

类加载器之间的父子关系和管辖范围图

[java]  view plain copy
  1. public class ClassLoaderTest {  
  2. public static void main(String[] args) {  
  3.     //获取当前类的类加载器  
  4.     ClassLoader loader=ClassLoaderTest.class.getClassLoader();  
  5.     //最终的类加载器是BootStrap,不是Java类,不能获取,为null  
  6.     while(loader!=null){  
  7.         System.out.println(loader.getClass().getName());//获取类加载器名  
  8.         loader=loader.getParent();//获取父类  
  9.     }  
  10.     System.out.println(loader);  
  11. }  
  12. }  

类加载器的委托机制

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

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

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

    4.每个类加载器加载类时,先委托给其上级加载器,当最终的类加载器没有加载到类时,向发起者类加载器逐级加载,如果直到发起者类加载器都没有加载到类,则抛ClassNotFoundException

编写自己的类加载器

    编程步骤:

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

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

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


练习:编写对class加密的工具类

要加密的类

[java]  view plain copy
  1. public class ClassLoaderAttachments extends Date {    
  2.     @Override    
  3.     public String toString() {    
  4.         return "hello,java";    
  5.     }    
  6. }   

自定义加载器

[java]  view plain copy
  1. public class MyClassLoader extends ClassLoader {    
  2.     
  3.     public static void main(String[] args)throws Exception {    
  4.         // TODO Auto-generated method stub    
  5.         String srcPath = args[0];    
  6.         String destDir = args[1];    
  7.         FileInputStream fis = new FileInputStream(srcPath);    
  8.         //获取目标目录路径  
  9.         String destFileName =srcPath.substring(srcPath.lastIndexOf('\\')+1);    
  10.         String destPath = destDir+"\\"+destFileName;    
  11.         FileOutputStream fos = new FileOutputStream(destPath);   
  12.        //加密操作  
  13.         cypher(fis, fos);    
  14.         fis.close();    
  15.         fos.close();      
  16.     }  
  17.     public static void cypher(InputStream ips,OutputStream ops) throws Exception{    
  18.         int b = -1;    
  19.         while((b=ips.read())!=-1){    
  20.             ops.write(b^0xff);    //文件加密              
  21.         }    
  22.      }    
  23.     private String classDir;    
  24.     @Override    
  25.     protected Class<?> findClass(String name) throws ClassNotFoundException {    
  26.         // 获取文件路径并解密  
  27.         String classFileName = classDir+"\\"+name+".class";    
  28.         try {    
  29.             FileInputStream fis = new FileInputStream(classFileName);    
  30.             ByteArrayOutputStream bos = new ByteArrayOutputStream();    
  31.             cypher(fis,bos);    
  32.             byte[] b = bos.toByteArray();               
  33.             return defineClass(b, 0, b.length);            
  34.         } catch (Exception e) {    
  35.             // TODO Auto-generated catch block    
  36.             e.printStackTrace();    
  37.         }        
  38.         return super.findClass(name);    
  39.     }    
  40.         
  41.     public MyClassLoader(){    
  42.             
  43.     }    
  44.     public MyClassLoader(String classDir){    
  45.         this.classDir = classDir;    
  46.             
  47.     }     
  48. }  

调用自己定义的加载器

[java]  view plain copy
  1. Class clazz = new MyClassLoader("itcastlib").loadClass("ClassLoaderAttachments");    
  2. Date d = (Date)clazz.newInstance();    
  3. System.out.println(d.toString());   

2.代理

     代理的作用:为已存在的多个具有相同接口的目标类的各个方法增加一些系统功能,如异常处理,计算运行时间等

动态代理技术
    1.JVM可以在运行期动态生成该类的字节码,这种动态生成的类往往被用作代理类,即动态代理类

    2.JVM生成的动态代理类必须实现一个或多个接口,所以JVM生成的动态类只能用作具有相同接口的目标类型的代理

    3.CGLIB库可以动态生成一个类的子类,一个类的子类也可以用作该类的代理,所以,如果要为一个没有实现接口的类生成动态代理类,那么可以使用CGLIB库

    4.代理类的各个方法中通常除了要调用目标的相应方法和对外返回目标返回的结果外,还可以在代理方法中的如下四个位置上加上系统功能代码:

a.在调用目标方法之前

b.在调用目标方法之后

c.在调用目标方法前后

d.在处理目标方法异常的catch块中


利用InvocationHandler接口创建代理类的三种形式

[java]  view plain copy
  1. //1.实现InvocationHandler接口  
  2. class MyInvocationHandler1 implements InvocationHandler{            
  3.     public Object invoke(Object proxy, Method method, Object[] args)    
  4.             throws Throwable {              
  5.         return null;    
  6.     }                   
  7. }           
  8. Collection proxy1 = (Collection)constructor.newInstance(new MyInvocationHandler1());    
  9. System.out.println(proxy1.toString());    
  10.   
  11. //2.通过匿名内部的形式实现  
  12. Collection proxy2 = (Collection)constructor.newInstance(new InvocationHandler(){        
  13.     public Object invoke(Object proxy, Method method, Object[] args)    
  14.             throws Throwable {                  
  15.         return null;    
  16.     }               
  17. });    
  18.   
  19.   
  20. //3.用Proxy的静态方法newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)  
  21. Collection proxy3=(Collection)Proxy.newProxyInstance(    
  22.         Collection.class.getClassLoader(),     
  23.         new Class[]{Collection.class},    
  24.         new InvocationHandler(){    
  25.             ArrayList target= new ArrayList();    
  26.             public Object invoke(Object proxy, Method method,    
  27.                     Object[] args) throws Throwable {    
  28.                 long startTime = System.currentTimeMillis();    
  29.                 Object retVal = method.invoke(target, args);    
  30.                 long endTime = System.currentTimeMillis();    
  31.                 System.out.println(method.getName()+"  running time:"+(endTime - startTime));    
  32.                 return retVal;                          
  33.             }    
  34.                 
  35.         });    
  36. proxy3.add("123");    
  37. proxy3.add("453");    
  38. proxy3.add("789");    
  39. System.out.println(proxy3.size());    

练习:编写一个代理

创建接口

[java]  view plain copy
  1. public interface Advice {    
  2.     void beforeMethod(Method method);    
  3.     void afterMethod(Method method);    
  4. }  

实现接口

[java]  view plain copy
  1. public class MyAdvice implements Advice {    
  2.     long startTime=0;    
  3.     @Override    
  4.     public void beforeMethod(Method method) {    
  5.          startTime = System.currentTimeMillis();    
  6.     }    
  7.     
  8.     @Override    
  9.     public void afterMethod(Method method) {      
  10.         long endTime = System.currentTimeMillis();    
  11.         System.out.println(method.getName()+"  running time:"+(endTime - startTime));    
  12.     }      
  13. }    

创建代理

[java]  view plain copy
  1. public static Object getProxy(final Object target,final Advice advice) {    
  2.     Object proxy3 =Proxy.newProxyInstance(    
  3.             target.getClass().getClassLoader(),  //类加载器  
  4.             target.getClass().getInterfaces(),//实现的接口  
  5.             new InvocationHandler() {                   
  6.             @Override    
  7.             public Object invoke(Object proxy, Method method, Object[] args)    
  8.                     throws Throwable {    
  9.                 advice.beforeMethod(method);    
  10.                 Object retVal = method.invoke(target, args);    
  11.                 advice.afterMethod(method);     
  12.                 return retVal;    
  13.             }    
  14.     });    
  15.     return proxy3;    
  16. }    

 ------- Windows Phone 7手机开发.Net培训、期待与您交流! -------

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值