您现在的位置:主页 > java教程 > >

Java中的ClassLoader详细解释



时间: 2014-08-17 22:07     来源 : iteye      点击:

关键词: java    jvm    ClassLoader   


      java中的.java文件经过编译以后,就会生成类文件.class文件。class文件是以二进制字节码存放在硬盘中的。当我们需要使用或加载Java文件到JVM中的时候,会从硬盘中读取字节码的class文件,然后通过类加载器将class文件加载到JVM中。也就是说,所有的Java文件都是通过类加载器加载到JVM中的。当然类加载器也是一个Java文件。那么第一个类加载器又是如何加载到JVM中的呢?在启动JVM的时候,会调运一个本地方法findBootStrapClass方法加载最初始的那个ClassLoader,private native Class findBootstrapClass(String name),这个本地方法使用C++编写的。

1.系统已有3种类加载器
    1.1 BooststrapClassLoader(boot) 加载rt.jar下面的类(此加载器采用C++编写,一般开发中是看不到的)  

内容来自www.itxxz.com


    1.2 ExtClassLoader  加载ExtClassLoader下面的类(ext文件夹下面的jar)
    1.3 AppClassLoader  加载classpaht下面的类
    我们写的类几乎都是通过AppClassLoader这个加载器加载到JVM中的。

2.类加载器的加载机制(双亲委托机制
    每一个类加载器都有一个对应的parentClassLoader。
    2.1    系统类加载器的父子关系
        自定义类加载器的父亲是AppClassLoader
        AppClassLoader的父亲是ExtClassLoader
        ExtClassLoader的父亲是BooststrapClassLoader

itxxz.com

Java代码  
  1. public class TestClassLoader {  
  2.     public static void main(String[] args) {  
  3.         // 当前对象的类加载器  
  4.         ClassLoader loader = new TestClassLoader().getClass().getClassLoader();  
  5.         // 从当前对象的类加载器想上找他的各个祖先  
  6.         while (loader != null) {  
  7.             System.out.println(loader.getClass().getName());  
  8.             loader = loader.getParent();  
  9.         }  
  10.         // 知道找到最后的祖先是null  
  11.         System.out.println(loader);  
  12.     }  
  13. }  
  14. 输出:  
  15. sun.misc.Launcher$AppClassLoader  
  16. sun.misc.Launcher$ExtClassLoader  
  17. null  

 

IT学习者(www.itxxz.com)

    2.2    类加载器加载的顺序
        类加载器是从根向下加载的
        也就是boot-》ExiClassLoader -》AppClassLoader
        当一个类需要被加载的时候,首先是由AppClassLoader加载器将其传递给其父亲ExtClassLoader,然后ExtClassLoader再将其传递给他的父亲boot。
        当boot发现他的加载范围内有对应的class,就加载到JVM中,否则交给儿子ExtClassLoader处理。
        ExtClassLoader再在他的加载范围类找有没有对应的class,有就加载到JVM中,没有就交给AppClassLoader处理。
        AppClassLoader再在classpath路径下找对应的class,找到就加载,没有就报异常。
        原因:这样可以保证JVM中某一个className对应的是同一个class,因为都是从根向下加载的。
copyright www.itxxz.com

        避免重复加载,当父亲已经加载了该类的时候,就没有必要子ClassLoader再加载一次。 
        要是从下向上加载,可能导致某一个className在JVM中对应好几个class。可能我们会定义自己的类加载器和自己的加载范围。
        当自己定义的类加载在他们各自的范围都发现需要加载的类,那么他们可能都会加载,导致JVM中一个className对应好几个不同的class
  
    2.3    比如我们自己定义一个类加载器去加载java.lang.String这个类,那么我们是不能达到我们目的的。
        因为加载机制是从上到下加载,当传递到上面的boot的时候,已经被加载到JVM中,轮不到我们自定义的类加载器去加载它。
        但是,我们肯定是可以自己定义一个类加载器去加载我们指定的类的。 本文来自www.itxxz.com

3.如何自定义一个类加载
    首先,我们需要继承ClassLoadar
    然后,我们不能破坏原来的类加载机制(双亲委托机制),所以我们不能覆盖loadClass方法,我们需要覆盖findclass方法。
    最后,在findClass方法中写入我们的类加载器的代码。
    查看源码解释: 本文来自www.itxxz.com

Java代码  
  1. protected Class<?> loadClass(String name, boolean resolve)  
  2.     throws ClassNotFoundException  
  3. {  
  4.     synchronized (getClassLoadingLock(name)) {  
  5.         // 首先检查name对应的Class是否已经被加载  
  6.         Class c = findLoadedClass(name);  
  7.         //如果没有被加载  
  8.         if (c == null) {  
  9.             long t0 = System.nanoTime();  
  10.             //尝试让parentClassLoader去加载  
  11.             try {  
  12.                 if (parent != null) {  
  13.                     //当parent不为null的时候,让parent去loadClass  
  14.                     c = parent.loadClass(name, false);  
  15.                 } else {  
  16.                     //当parent为null的时候,就调运本地方法  
  17.                     c = findBootstrapClassOrNull(name);  
  18.                 }  
  19.             } catch (ClassNotFoundException e) {  
  20.              
  21.             }  
  22.             //当parentClassLoader没有加载的时候  
  23.             if (c == null) {  
  24.                 long t1 = System.nanoTime();  
  25.                 //调运findClass方法去加载  
  26.                 c = findClass(name);  
  27.             }  
  28.         }  
  29.         if (resolve) {  
  30.             resolveClass(c);  
  31.         }  
  32.         return c;  
  33.     }  
  34. }  

  copyright www.itxxz.com
4.举例
    目标:自定义一个类加载器加我们指定路径下,经过我么加密的class。
    过程:找到指定路径下的class文件,解密,加载到JVM中。
    
    4.1先定义一个需要被加密编译的类,同时使用它进行测试 copyright www.itxxz.com

Java代码  
  1. public class MyClass extends Date {  
  2.     @Override  
  3.     public String toString() {  
  4.         return "hello world";  
  5.     }  
  6. }  

      本文来自www.itxxz.com
    4.2加密原来的class文件 内容来自www.itxxz.com

Java代码  
  1. public static void main(String[] args) throws Exception {  
  2.     String inputSrc = args[0];//原来的class文件的路径和文件名  
  3.     String outputSrc = args[1];//加密以后存放的文件路径和文件名  
  4.     FileInputStream fis = new FileInputStream(inputSrc);  
  5.     FileOutputStream fos = new FileOutputStream(outputSrc);  
  6.     //调用加密算法  
  7.     cypher(fis, fos);  
  8.     fis.close();  
  9.     fos.close();  
  10. }  
  11.   
  12. /** 
  13.  * 加密解密函数方法 
  14.  * 
  15.  * @param is 
  16.  * @param os 
  17.  * @throws IOException 
  18.  */  
  19. private static void cypher(InputStream is, OutputStream os) throws IOException {  
  20.     int b = -1;  
  21.     while ((b = is.read()) != -1) {  
  22.         // 将0变成1,将1变成0  
  23.         os.write(b ^ 0xff);  
  24.     }  
  25. }  

 

本文来自www.itxxz.com

    4.3编写我们自己的类加载器 本文来自www.itxxz.com

Java代码  
  1. public class MyClassLoader extends ClassLoader {  
  2.     //要加载的类的路径  
  3.     private String classSrc;  
  4.   
  5.     public MyClassLoader() {  
  6.   
  7.     }  
  8.   
  9.     public MyClassLoader(String classSrc) {  
  10.         this.classSrc = classSrc;  
  11.     }  
  12.   
  13.     @Override  
  14.     protected Class<?> findClass(String name) throws ClassNotFoundException {  
  15.         //先找到自己的加密的class文件的位置  
  16.         String classFielName = classSrc + "\\" + name + ".class";  
  17.         try {  
  18.             FileInputStream fis = new FileInputStream(classFielName);  
  19.             ByteArrayOutputStream baos = new ByteArrayOutputStream();  
  20.             //调运解密算法  
  21.             cypher(fis, baos);  
  22.             fis.close();  
  23.             byte[] bytes = baos.toByteArray();  
  24.             //将读出来的二进制转换为Class字节码  
  25.             return defineClass(null, bytes, 0, bytes.length);  
  26.         } catch (Exception e) {  
  27.             e.printStackTrace();  
  28.         }  
  29.   
  30.         return super.findClass(name);  
  31.     }  
  32. }  

  copyright www.itxxz.com
    4.4使用我们自己的类加载加载我们加密的类到JVM中 官网:http://www.itxxz.com

Java代码  
  1. //首先使用自己的类加载器去加载我们加密的class文件  
  2. //注意,这个地方的加载类的路径下的class应该是我们加密以后文件的位置  
  3. Class clazz = new MyClassLoader("E:/AllWorkspace/workspace1/classLoaderTest/bin/com/gusi/test").loadClass("MyClass");  
  4. //通过反射,测试我们的classLoader  
  5. Date date = (Date) clazz.newInstance();  
  6. System.out.println(date.toString());  
(via:孤思八九)






文章除注明转载外,均为IT学习者原创或编译
欢迎任何形式的转载,但务必请以超链接形式注明出处
本文出自:iteye
链接地址:http://www.itxxz.com



微信公众号:喝咖啡的螃蟹

喝咖啡的螃蟹
评论列表(网友评论仅供网友表达个人看法,并不表明本站同意其观点或证实其描述)
  • 领现金
    2017-03-20 20:41:28发表

    送88元 凱旋门 A8娱乐城 皇浦国际 菲彩国际 V5娱乐城 ➀➀➀➇Ⓣ.ⒸⓄⓂ

  • 奢侈品代购网
    2017-03-20 00:03:36发表

    a货网 Balenciaga(巴黎世家) Armani(阿玛尼) Ann Demeulemeester(安●迪穆拉米斯特) Tefei(特妃) Sotirio Bulgari(宝格丽) DSQUARED2(第二次方) yuandan.gq