美文网首页
JavaSE知识点17java类加载器详解

JavaSE知识点17java类加载器详解

作者: paulpaullong | 来源:发表于2017-04-04 17:17 被阅读0次

    1 什么是ClassLoader?

    • 1 大家都知道,当我们写好一个Java程序之后,不是管是CS还是BS应用,都是由若干个.class文件组织而成的一个完整的Java应用程序,当程序在运行时,即会调用该程序的一个入口函数来调用系统的相关功能,而这些功能都被封装在不同的class文件当中,所以经常要从这个class文件中要调用另外一个class文件中的方法,如果另外一个文件不存在,则会引发系统异常。
    • 2 而程序在启动的时候,并不会一次性加载程序所要用的所有class文件,而是根据程序的需要,通过Java的类加载机制(ClassLoader)来动态加载某个class文件到内存当中的,从而只有class文件被载入到了内存之后,才能被其它class所引用。所以ClassLoader就是用来动态加载class文件到内存当中用的。

    2 Java提供的三个默认ClassLoader

    • 1 BootStrap ClassLoader:称为启动类加载器,是Java类加载层次中最顶层的类加载器,负责加载JDK中的核心类库,如:rt.jar、resources.jar、charsets.jar等,可通过查找sun.boot.class.path这个系统属性,得知该类加载器从哪些地方加载了相关的jar或class文件。
    System.out.println(System.getProperty("sun.boot.class.path"));  
    

    输出结果为:

    C:\Program Files\Java\jdk1.6.0_22\jre\lib\resources.jar;
    C:\Program Files\Java\jdk1.6.0_22\jre\lib\rt.jar;
    C:\Program Files\Java\jdk1.6.0_22\jre\lib\sunrsasign.jar;
    C:\Program Files\Java\jdk1.6.0_22\jre\lib\jsse.jar;
    C:\Program Files\Java\jdk1.6.0_22\jre\lib\jce.jar;
    C:\Program Files\Java\jdk1.6.0_22\jre\lib\charsets.jar;
    C:\Program Files\Java\jdk1.6.0_22\jre\classes
    
    • 2 Extension ClassLoader:称为扩展类加载器,负责加载Java的扩展类库,默认加载JAVA_HOME/jre/lib/ext/目下的所有jar。
    • 3 App ClassLoader:称为系统类加载器,负责加载应用程序classpath目录下的所有jar和class文件。
    • 4 总结
      Extension ClassLoader和App ClassLoader也继承自java.lang.ClassLoader类,但是Bootstrap ClassLoader不继承自java.lang.ClassLoader类,因为它不是一个普通的Java类,底层由C++编写,已嵌入到了JVM内核当中,当JVM启动后,Bootstrap ClassLoader也随着启动,负责加载完核心类库后,并构造Extension ClassLoader和App ClassLoader类加载器。

    3 ClassLoader加载类的原理

    • 1 原理简介
      1)ClassLoader使用的是双亲委托模型来搜索类的,每个ClassLoader实例都有一个父类加载器的引用(不是继承的关系,是一个包含的关系)。
      2)类加载器的包含关系如下:
      自定义类加载器————>系统类加载器————>扩展类加载器————>根加载器
      3)当一个自定义ClassLoader实例需要加载某个类时,先从自己的命名空间里查找是否已加载该类,如果已加载,则直接返回代表该类的Class对象的引用。
      4)如果没加载,才委托其父类加载器进行加载。其父类加载器命名空间没有在,则一层层向上委托,直到根加载器。然后由根加载器开始从上到下加载该类。一旦加载到则将这个找到的类生成一个类的定义,并将它加载到内存当中,最后返回这个类在内存中的Class实例对象的引用。
      5)如果所有加载器都没有加载到这个类时,则抛出ClassNotFoundException异常。
    • 2 为什么要使用双亲委托这种模型呢?
      1)这样可以避免重复加载,当父亲已经加载了该类的时候,就没有必要子ClassLoader再加载一次。
      2)考虑到安全因素,我们试想一下,如果不使用这种委托模式,那我们就可以随时使用自定义的String来动态替代java核心api中定义的类型,这样会存在非常大的安全隐患,而双亲委托的方式,就可以避免这种情况,因为String已经在启动时就被引导类加载器(Bootstrcp ClassLoader)加载,所以用户自定义的ClassLoader永远也无法加载一个自己写的String,除非你改变JDK中ClassLoader搜索类的默认算法。
    • 3 JVM在搜索类的时候,又是如何判定两个class是相同的呢?
      1)JVM在判定两个class是否相同时,不仅要判断两个类名是否相同,而且要判断是否由同一个类加载器实例加载的。只有两者同时满足的情况下,JVM才认为这两个class是相同的。就算两个class是同一份class字节码,如果被两个不同的ClassLoader实例所加载,JVM也会认为它们是两个不同class。
      2)比如网络上的一个Java类,javac编译之后生成字节码文件,同一个自定义类加载器的两个实例同时读取了这个字节码文件,并分别定义出了java.lang.Class实例来表示这个类,对于JVM来说,它们是两个不同的实例对象,但它们确实是同一份字节码文件,如果试图将这个Class实例生成具体的对象进行转换时,就会抛运行时异常java.lang.ClassCaseException,提示这是两个不同的类型。

    4 定义自已的ClassLoader

    • 1 既然JVM已经提供了默认的类加载器,为什么还要定义自已的类加载器呢?
      因为Java中提供的默认ClassLoader,只加载指定目录下的jar和class,如果我们想加载其它位置的类或jar时,比如:我要加载网络上的一个class文件,通过动态加载到内存之后,要调用这个类中的方法实现我的业务逻辑。在这样的情况下,默认的ClassLoader就不能满足我们的需求了,所以需要定义自己的ClassLoader。
    • 2 定义自已的类加载器分为两步:
      1)继承java.lang.ClassLoader
      2)重写父类的findClass方法
    • 3 父类有那么多方法,为什么偏偏只重写findClass方法?
      因为JDK已经在loadClass方法中帮我们实现了ClassLoader搜索类的算法,当在loadClass方法中搜索不到类时,loadClass方法就会调用findClass方法来搜索类,所以我们只需重写该方法即可。
    • 4 代码举例
    package lwlstudy.test.classloader;
    import java.io.ByteArrayOutputStream;
    import java.io.File;
    import java.io.FileInputStream;
    import java.io.IOException;
    import java.io.InputStream;
    public class MyClassLoader extends ClassLoader {
    
        private String classPath;
    
        public MyClassLoader(String classPath) {
            this.classPath = classPath;
        }
    
        public String getClassPath() {
            return classPath;
        }
    
        public void setClassPath(String classPath) {
            this.classPath = classPath;
        }
    
        public MyClassLoader(ClassLoader parent) {
            super(parent);
        }
    
        @Override
        protected Class<?> findClass(String className) throws ClassNotFoundException {
            byte[] data = this.getData(className);
            if (data == null) {
                throw new ClassNotFoundException();
            } else {
                return defineClass(className, data, 0, data.length);
            }
        }
    
        private byte[] getData(String className) {
    
            String path = classPath + File.separatorChar + className.replace('.', File.separatorChar) + ".class";
            InputStream is = null;
            ByteArrayOutputStream stream = null;
            try {
                is = new FileInputStream(new File(path));
                stream = new ByteArrayOutputStream();
                byte[] buffer = new byte[2048];
                int num = 0;
                while ((num = is.read(buffer)) != -1) {
                    stream.write(buffer, 0, num);
                }
                return stream.toByteArray();
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                try {
                    is.close();
                    stream.close();
                } catch (IOException e2) {
                    e2.printStackTrace();
                }
            }
    
            return null;
        }
    
        public static void main(String[] args) throws Exception {
    
            MyClassLoader mcl1 = new MyClassLoader("W:\\zz");
            Class<?> clazz = mcl1.loadClass("lwlstudy.test.classloader.Dog");
            Object obj = clazz.newInstance();
            Object obj1 = clazz.newInstance();
            System.out.println(obj.getClass().equals(obj1.getClass()));
    
            MyClassLoader mcl2 = new MyClassLoader("W:\\zz");
            Class<?> clazz2 = mcl2.loadClass("lwlstudy.test.classloader.Dog");
            Object obj2 = clazz2.newInstance();
    
            System.out.println(obj.getClass().equals(obj2.getClass()));
        }
    }
    ```
    输出结果为:
    ```java
    true
    false
    ```
    原因参考:如何判定两个class是相同的呢?

    相关文章

      网友评论

          本文标题:JavaSE知识点17java类加载器详解

          本文链接:https://www.haomeiwen.com/subject/akczottx.html