首页 > 代码库 > Java中的ClassLoader具体解说

Java中的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++编写,通常开发中是看不到的)  

    1.2 ExtClassLoader  加载ExtClassLoader下面的类(ext文件夹下面的jar)

    1.3 AppClassLoader  加载classpaht下面的类

    咱们写的类简直都是经过AppClassLoader这个加载器加载到JVM中的。


2.类加载器的加载机制(双亲托付机制)

    每一个类加载器都有一个对应的parentClassLoader。

    2.1    体系类加载器的父子关系

        自界说类加载器的爸爸是AppClassLoader

        AppClassLoader的爸爸是ExtClassLoader

        ExtClassLoader的爸爸是BooststrapClassLoader

Java代码  保藏代码

public class TestClassLoader {  

    public static void main(String[] args) {  

        // 当时目标的类加载器  

        ClassLoader loader = new TestClassLoader().getClass().getClassLoader();  

        // 从当时目标的类加载器想上找他的各个祖先  

        while (loader != null) {  

            System.out.println(loader.getClass().getName());  

            loader = loader.getParent();  

        }  

        // 晓得找到最终的祖先是null  

        System.out.println(loader);  

    }  

}  

输出:  

sun.misc.Launcher$AppClassLoader  

sun.misc.Launcher$ExtClassLoader  

null  

 

    2.2    类加载器加载的次序

        类加载器是从根向下加载的

        也即是boot-》ExiClassLoader -》AppClassLoader

        当一个类需求被加载的时分,首要是由AppClassLoader加载器将其传递给其爸爸ExtClassLoader,然后ExtClassLoader再将其传递给他的爸爸boot。

        当boot发现他的加载规模内有对应的class,就加载到JVM中,否则交给儿子ExtClassLoader处置。

        ExtClassLoader再在他的加载规模类找有没有对应的class,有就加载到JVM中,没有就交给AppClassLoader处置。

        AppClassLoader再在classpath途径下找对应的class,找到就加载,没有就报反常。

        缘由:这样能够确保JVM中某一个className对应的是同一个class,由于都是从根向下加载的。

        避免重复加载,当爸爸现已加载了该类的时分,就没有必要子ClassLoader再加载一次。 

        要是从下向上加载,能够致使某一个className在JVM中对应好几个class。能够咱们会界说自个的类加载器和自个的加载规模。

        当自个界说的类加载在他们各自的规模都发现需求加载的类,那么他们能够都会加载,致使JVM中一个className对应好几个不一样的class

  

    2.3    比方咱们自个界说一个类加载器去加载java.lang.String这个类,那么咱们是不能到达咱们目的的。

        由于加载机制是从上到下加载,当传递到上面的boot的时分,现已被加载到JVM中,轮不到咱们自界说的类加载器去加载它。

        但是,咱们肯定是能够自个界说一个类加载器去加载咱们指定的类的。


3.怎么自界说一个类加载

    首要,咱们需求承继ClassLoadar

    然后,咱们不能破坏本来的类加载机制(双亲托付机制),所以咱们不能掩盖loadClass办法,咱们需求掩盖findclass办法。

    最终,在findClass办法中写入咱们的类加载器的代码。

    查看源码解说:

Java代码  保藏代码

protected Class loadClass(String name, boolean resolve)  

    throws ClassNotFoundException  

{  

    synchronized (getClassLoadingLock(name)) {  

        // 首要查看name对应的Class是不是现已被加载  

        Class c = findLoadedClass(name);  

        //假如没有被加载  

        if (c == null) {  

            long t0 = System.nanoTime();  

            //测验让parentClassLoader去加载  

            try {  

                if (parent != null) {  

                    //当parent不为null的时分,让parent去loadClass  

                    c = parent.loadClass(name, false);  

                } else {  

                    //当parent为null的时分,就调运本地办法  

                    c = findBootstrapClassOrNull(name);  

                }  

            } catch (ClassNotFoundException e) {  

             

            }  

            //当parentClassLoader没有加载的时分  

            if (c == null) {  

                long t1 = System.nanoTime();  

                //调运findClass办法去加载  

                c = findClass(name);  

            }  

        }  

-        indexRead arguments from command-line "http://www.shoudashou.com"

-        indexRead arguments from command-line "http://www.4lunwen.cn"

-        indexRead arguments from command-line "http://www.zx1234.cn"

-        indexRead arguments from command-line "http://www.penbar.cn"

-        indexRead arguments from command-line "http://www.whathappy.cn"

-        indexRead arguments from command-line "http://www.lunjin.net"

-        indexRead arguments from command-line "http://www.ssstyle.cn"

-        indexRead arguments from command-line "http://www.91fish.cn"

-        indexRead arguments from command-line "http://www.fanselang.com"

        if (resolve) {  

            resolveClass(c);  

        }  

        return c;  

    }  

}  

 

4.举例

    目标:自界说一个类加载器加咱们指定途径下,经过我么加密的class。

    进程:找到指定途径下的class文件,解密,加载到JVM中。

    

    4.1先界说一个需求被加密编译的类,一起运用它进行测验

Java代码  保藏代码

public class MyClass extends Date {  

    @Override  

    public String toString() {  

        return "hello world";  

    }  

}  

     

    4.2加密本来的class文件

Java代码  保藏代码

public static void main(String[] args) throws Exception {  

    String inputSrc = http://www.mamicode.com/args[0];//本来的class文件的途径和文件名

    String outputSrc = http://www.mamicode.com/args[1];//加密今后寄存的文件途径和文件名

    FileInputStream fis = new FileInputStream(inputSrc);  

    FileOutputStream fos = new FileOutputStream(outputSrc);  

    //调用加密算法  

    cypher(fis, fos);  

    fis.close();  

    fos.close();  

}  

  

/** 

 * 加密解密函数办法 

 * 

 * @param is 

 * @param os 

 * @throws IOException 

 */  

private static void cypher(InputStream is, OutputStream os) throws IOException {  

    int b = -1;  

    while ((b = is.read()) != -1) {  

        // 将0成为1,将1成为0  

        os.write(b ^ 0xff);  

    }  

}  

 

    4.3编写咱们自个的类加载器

Java代码  保藏代码

public class MyClassLoader extends ClassLoader {  

    //要加载的类的途径  

    private String classSrc;  

  

    public MyClassLoader() {  

  

    }  

  

    public MyClassLoader(String classSrc) {  

        this.classSrc = http://www.mamicode.com/classSrc;

    }  

  

    @Override  

    protected Class findClass(String name) throws ClassNotFoundException {  

        //先找到自个的加密的class文件的方位  

        String classFielName = classSrc + "\\" + name + ".class";  

        try {  

            FileInputStream fis = new FileInputStream(classFielName);  

            ByteArrayOutputStream baos = new ByteArrayOutputStream();  

            //调运解密算法  

            cypher(fis, baos);  

            fis.close();  

            byte[] bytes = baos.toByteArray();  

            //将读出来的二进制转换为Class字节码  

            return defineClass(null, bytes, 0, bytes.length);  

        } catch (Exception e) {  

            e.printStackTrace();  

        }  

  

        return super.findClass(name);  

    }  

}  

 

    4.4运用咱们自个的类加载加载咱们加密的类到JVM中

Java代码  保藏代码

//首要运用自个的类加载器去加载咱们加密的class文件  

//留意,这个当地的加载类的途径下的class应该是咱们加密今后文件的方位  

Class clazz = new MyClassLoader("E:/AllWorkspace/workspace1/classLoaderTest/bin/com/gusi/test").loadClass("MyClass");  

//经过反射,测验咱们的classLoader  

Date date = (Date) clazz.newInstance();  

System.out.println(date.toString());