• JVM类加载器


    类加载器的作用不仅仅是实现类的加载,它还与类的的“相等”判定有关,关系着Java“相等”判定方法的返回结果,只有在满足如下三个类“相等”判定条件,才能判定两个类相等。

    1、两个类来自同一个Class文件

    2、两个类是由同一个虚拟机加载

    3、两个类是由同一个类加载器加载

    JVM类加载器

    启动类加载器(bootstrap classLoader):启动类加载器,负责加载java的核心类库,加载如(%JAVA_HOME%/lib)下的rt.jar(包含System,String等核心类)这样的核心类库。根类加载器不是classLoader的子类,它是JVM自身内部由C/C++实现的,并不是Java实现的。

    扩展类加载器(Extension classLoader):扩展类加载器,负责加载扩展目录(%JAVA_HOME%/jre/lib/ext)下的jar包,用户可把自己开发的类的jar放入ext目录下,即可扩展除核心类以为的新功能。

    系统类加载器(Application classLoader):系统类加载器或称为应用程序类加载器,是加载CLASSPATH环境变量所指定的jar包与类路径。一般来说,用户自定义的类就是由APP ClassLoader加载的。

    类加载器的双亲委派模型机制

    当一个类收到了类加载的请求,他首先不会自己尝试加载这个类,而是将这个请求委派给父类加载器来完成,父类加载器收到请求后,也会找到找到其父类加载器。所以类加载的请求都会传到bootstrap classLoader,只有当父类加载器无法加载时(在其类加载路径中找不到所需加载的class),子类加载器才会尝试自己去加载。

    这个过程如下图标号过程所示:(借图,哈哈)

    双亲委派的源码实现,先检查是否有被加载过,如果没有,则调用父类的类加载方法,若父类加载器为空,则尝试通过bootstrap classLoader来加载,如果还是未加载成功,则调用自身的加载方法,

        protected synchronized Class loadClass(String name, boolean resolve)
                throws ClassNotFoundException {
            // 首先检查该name指定的class是否有被加载
            Class c = findLoadedClass(name);
            if (c == null) {
                try {
                    if (parent != null) {
                        // 如果parent不为null,则调用parent的loadClass进行加载
                        c = parent.loadClass(name, false);
                    } else {
                        // parent为null,则调用BootstrapClassLoader进行加载
                        c = findBootstrapClass0(name);
                    }
                } catch (ClassNotFoundException e) {
                    // 如果仍然无法加载成功,则调用自身的findClass进行加载
                    c = findClass(name);
                }
            }
            if (resolve) {
                resolveClass(c);
            }
            return c;
        }
    

      双亲委派模型验证

    public class ClassLoaderTest {
    
        public static void main(String[] args){
    //输出ClassLoaderTest类加载的名称
            System.out.println("ClassLoaderTest class loader is "+ClassLoaderTest.class.getClassLoader().getClass().getName());
    //输出System类的类加载器
            System.out.println("System class loader is "+System.class.getClassLoader());
    //输出List类的类加载器
            System.out.println("List class loader is "+List.class.getClassLoader());
    
            ClassLoader c1=ClassLoaderTest.class.getClassLoader();
            while(c1!=null){//递归获取类加载器的父类加载器
    
                System.out.print(c1.getClass().getName()+"->");
                c1=c1.getParent();
            }
    
            System.out.println(c1);
    
        }
    }
    

      输出结果为:

    ClassLoaderTest class loader is sun.misc.Launcher$AppClassLoader
    System class loader is null
    List class loader is null
    sun.misc.Launcher$AppClassLoader->sun.misc.Launcher$ExtClassLoader->null

    解释:

    ClassLoaderTest的类加载器为AppClassLoader,即ClassLoaderTest类是用户定义的类,位于CLASSPATH下,由系统/应用程序类加载器加载

    System与List为核心类,有boostrap classLoader加载,而启动类加载器是在JVM内部通过C/C++实现的,并不是通过Java,自然也就不能继承classLoader类,所有不能输出其名称。

    箭头代表的为类加载器的委托过程,委托到ExtClassLoader,再委托到启动类加载器,最后通过AppClassLoader加载成功。

    做个假设,如果将ClassLoaderTest类打包好放入到lib/ext目录下,输出结果会变成什么样?

    第一个输出应该就为扩展类加载器二不是应用程序类加载器

    最后一个输出应该为 sun.misc.Launcher$ExtClassLoader->null

    解释:

    jar包放入到ext目录下后,通过双亲委派模型,启动类加载器加载失败后,会有扩展类加载器加载,扩展类加载器加载成功后就不需要再继续加载了。

    自定义加载类

    若要实现自定义类加载器,只需要继承java.lang.ClassLoader 类,并且重写其findClass()方法即可。java.lang.ClassLoader 类的基本职责就是根据一个指定的类的名称,找到或者生成其对应的字节代码,然后从这些字节代码中定义出一个 Java 类,即 java.lang.Class 类的一个实例。除此之外,ClassLoader 还负责加载 Java 应用所需的资源,如图像文件和配置文件等,ClassLoader 中与加载类相关的方法如下:

     
    方法                                 说明
    getParent()  返回该类加载器的父类加载器。

    loadClass(String name) 加载名称为 二进制名称为name 的类,返回的结果是 java.lang.Class 类的实例。

    findClass(String name) 查找名称为 name 的类,返回的结果是 java.lang.Class 类的实例。

    findLoadedClass(String name) 查找名称为 name 的已经被加载过的类,返回的结果是 java.lang.Class 类的实例。

    resolveClass(Class<?> c) 链接指定的 Java 类。

    能不能自己写个类叫java.lang.System?

    答案:通常不可以。 
    解释:类加载器的双亲委派模型,即使实现System类,但是加载的时候还是会有启动类加载器先加载,而启动类加载器会加载自带的System类,所以自己写的类就不会被加载。

    但是,我们可以自己定义一个类加载器来达到这个目的,为了避免双亲委托机制,这个类加载器也必须是特殊的。由于系统自带的三个类加载器都加载特定目录下的类,如果我们自己的类加载器放在一个特殊的目录,那么系统的加载器就无法加载,也就是最终还是由我们自己的加载器加载。

  • 相关阅读:
    PHP基础之文件的上传与下载
    PHP封装 文件上传
    PHP基础之文件操作
    Session案例:实现用户登录
    PHP基础之会话技术
    PHP基础之超全局变量
    PHP基础之HTTP协议
    PHP基础之错误处理及调试
    PHP基础之包含文件
    剑指offer-复杂链表的复制
  • 原文地址:https://www.cnblogs.com/dpains/p/7201554.html
Copyright © 2020-2023  润新知