`
nigel521
  • 浏览: 11486 次
社区版块
存档分类
最新评论

黑马程序员——类加载器及其委托机制的深入分析

 
阅读更多

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

类加载器及其委托机制的深入分析。
(1),由来:JVM将类的字节码(编译好的class文件)加载到内存中。
这是一件很重要的事,这件事是由JVM分配给类加载器在做。
(2),分类:JVM可以有多个加载器。系统默认了3个主要的类加载器。 BootStrap(不是java类),ExtClassLoader,AppClassLoader 负责加载特定位置的类。
   (3),特殊:BootStrap。竟然类加载器也是java类,在用到它们的时候。同样,也需要类加载器进行加载。很显然,这个时候就需要一个不是Java类的加载器。需要找到这个鼻祖,充当这个鼻祖的就是:BootStrap。它不是java类。
   (4),关系:
 
每个加载器只能加载指定位置的类。也可以自己定义一个加载器,指定加载
的位置。级别关系:BootStrap->ExtClassLoader->AppClassLoader
体现这种关系的方法是:ClassLoader里面的getParent()方法。

(5),委托
原理,每个类加载器加载类时,都会委托给它的上级加载器。当所有的祖宗都没法加载这个类的时候,就会回到发起者类加载器。还是加载不了,就会抛出
ClassNotFoundException。不会再去找发起者类加载器的儿子,因为没有getChild方法。
好处,可以集中管理。上级加载完后,下级直接哪来用就行了。否则,会加载很多已经加载的类。
面试题:写一个java.lang.System
(6),指定加载器。
首先当前线程的类加载器去加载线程中的第一个类。如果类A中引用了类B,Java虚拟机将使用加载类A的类装载器来加载类B。还可以直接调用ClassLoader对象的.loadClass()方法来用当前的类加载器去加载某个类。

自定义类加载器的编写原理分析。
目标:自定义一个类加载器,并指定加载位置。保证该位置中的类都能被自定义类加载器加载。并且这些类的字节码都有进行加密处理。加载过程中进行解密。
设计模式:模版方法设计模式,父类指定处理大纲,子类规定具体内容的设计模式。一般,在父类中声明子类需要重写的抽象方法(findClass),并且父类中存在一个方法(loadClass)调用了这些方法,保证事件的流程来完成一件相对的大事。

返回字节码:defineClass
 
protected  Class<?> defineClass(String name, byte[] b, int off, int len, ProtectionDomain protectionDomain)
          使用可选的 ProtectionDomain 将一个 byte 数组转换为 Class 类的实例。 
只要得到字节码,完成这一步就算成功了。

 

编写对class文件进行加密的工具类。
(1),加密方法:
// 定义一个加密方法,对ClassLoaderAttachment进行加密。
 public static void cypher(InputStream in, OutputStream out)
   throws Exception {
  int b = -1;
  while ((b = in.read()) != -1) {
   b = b ^ 0xff;
   out.write(b);
  }
  in.close();
  out.close();
 }

(2),findClass方法:
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
  String classPath = classDir+"\\"+name.subString(name.lastIndexOf(‘.’)+1)+".class";
  FileInputStream fis = null;
  ByteArrayOutputStream fos = null;
  try {
    fis = new FileInputStream(classPath);
    fos = new ByteArrayOutputStream();
    cypher(fis,fos);
    byte[] buf = fos.toByteArray();
//    byte[] buf = new byte[fis.available()];
//    fis.read(buf);
   
//    读取class文件,把读取的字节转换成字节码到内存中。
    return defineClass(buf, 0, buf.length);
  } catch (FileNotFoundException e) {
   e.printStackTrace();
  } catch (IOException e) {
   e.printStackTrace();
  } catch (Exception e) {
   e.printStackTrace();
  }finally{
   if(fis!=null){
    try {
     fis.close();
    } catch (IOException e) {
     e.printStackTrace();
    }
   }
  }
  return super.findClass(name);
 }

(3),调用:
 Class clazz = new MyClassLoader("classloaderlib").loadClass("classloader.ClassLoaderAttachment");
  ClassLoader loader = clazz.getClassLoader();
  while(loader!=null){
   System.out.println(loader.getClass().getName());
   loader = loader.getParent();
  }
//  ClassLoaderAttachment cla = (ClassLoaderAttachment) clazz.newInstance();
  System.out.println(loader);


 

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics