• JAVA基础_类加载器


    什么是类加载器

    • 类加载器是Java语言在1.0版本就引入的。最初是为了满足JavaApplet需要。现在类加载器在Web容器和OSGI中得到了广泛的应用,一般来说,Java应用的开发人员不需要直接同类加载器进行交互。Java虚拟机默认的行为就已经足够满足大多数情况的需求了。不过如果遇到了需要与类加载器进行交互的情况,而对类加载器的机制又不是很了解的话,就很容易花大量的时候在ClassNotFoundException和NoClassDefFoundError等异常上。
    • 顾名思义,类加载器是用来加载Java类到Java虚拟机中。一般来说,Java虚拟机使用Java类的方式如下:Java源程序(.java文件)在经过Java编译器编译之后会被转换成Java字节码代码(.class文件)。类加载器负责读取Java字节代码,并转换成java.lang.Class类的一个实例。每个这样的实例用来表示一个Java类。通过此实例的newInstance()方法就可以创建出该类的一个对象。实际的情况可能更复杂,比如Java字节码可能是通过工具动态生成的,也可能是通过网络下载的。基本上所有的类加载器都是java.lang.ClassLoader类的一个实例。

    ClassLoader类介绍

    java.lang.ClassLoader类的基本职责就是根据一个指定的类的名称,找到或者生成其对应的字节代码,然后从这些字节代码中定义出一个Java类,即Java.lang.Class类的一个实例,除此之外,ClassLoader还负责加载Java应用所需要的资源,如图像文件和配置文件。为了完成加载类这个职责,ClassLoader提供了一系列的方法。

    表1:ClassLoader中与加载类相关的方法

    方法
    getParent() 返回该类的父类加载器
    loadClass(String name) 加载名称为name的类,返回的结果是java.lang.Class类的实例
    findClass(String name)查找名称为name的类,返回的结果是java.lang.Class类的实例
    findLoadedClass(String name) 查找名称为name的已经被加载过的类,返回的结果是java.lang.Class的实例
    defineClass(String name,byte[] b,int off,int len)把字节数组b中的内容转换成java类,返回的结果是java.lang.Class的实例。这个方法被声明为final的
    resolveClass(Class<?> c)链接制定的Java类

    在表1中给出的方法,表示类名称的name参数的值是类的二进制名称。需要注意的是内部类的表示,如:com.exampe.Husband$Wife和com.example.OutClass$InnerClass等表示形式。

    类加载器的树状组织结构

    Java中的类加载器大致可以分为两类,一类是系统提供的,另外一类是Java应用开发人员自己编写的。系统提供的主要有以下三种:

    • 引导类加载器(bootstrap class loader):它用来加载Java的核心库,是用原生代码来实现的,并不集成自java.lang.ClassLoader。加载核心库JAVA_HOME/jre/lib/rt.jar或sun.boot.class.path下的内容
    • 扩展类加载器(extensions class loader):它用来加载Java的扩展库。Java虚拟机的实现会提供一个扩展库目录。该类加载器在此目录里面查找并加载Java类。加载扩展库JAVA_HOME/jre/ext/*.jar或java.ext.dirs路径下的内容
    • 系统类加载器(system class loader):他根据Java应用的类路径(CLASSPATH)来加载Java类。一般来说,Java应用的类都是由它来完成加载的。可以通过ClassLoader.getSystemClassLoader();来获取它。根据java应用的类路径(classpath,java.class.path)路径加载

    除了系统提供的类加载器以外,开发人员可以通过继承 java.lang.ClassLoader类的方式实现自己的类加载器,以满足一些特殊的需求。

    图1.类加载器树状结构示意图

    image

    类加载器的树状组织结构代码清单:ClassLoaderTree .java

    代码清单1:

    public class ClassLoaderTree {
        
        public static void main(String[] args) {
            ClassLoader classLoader = ClassLoaderTree.class.getClassLoader();
            while (classLoader != null) {
                System.out.println("classLoader = " + classLoader);
                // 找到其父加载器
                classLoader = classLoader.getParent();
            }
            System.out.println("classLoader = " + classLoader);
        }
    
    }

    输出结果1:

    image

    这也正好验证了类加载器的父子关系。每个Java类都维护一个指向定义它的类加载器的引用。通过getClassLoader()方法就可以获取到此引用。代码清单1通过递归调用getParent()方法来输出全部的父类加载器。在输出结果1中第一个输出的是ClassLoaderTree类的类加载器,即系统类加载器,是sun.misc.Launcher$AppClassLoader的实例,第二个输出的是扩展类加载器,是sun.misc.Launcher$ExtClassLoader的实例。在Java虚拟机中,引导类加载器是null,由启动时默认加载。

    测试ExtClassLoader

    • 导出至可执行jar包

    SNAGHTML16815686


    SNAGHTML16841a4a

    然后直接Finish就可以了。

    测试代码清单1的执行结果

    输出结果2:

    image

    我们可以看到,少了一个系统类加载器,这是为什么呢?这就和类加载器的机制问题有关了,就是代理模式,下面就将开始类加载器的代理模式的笔记记录。

    类加载器的代理模式

    • 代理模式
      • 交给其他加载器来加载指定的类
    • 双亲委托机制
      • 在某个特定的类加载器在接到加载类的请求时,自己本身先不去执行加载任务,而是将加载任务委托给其父类加载器,一次追溯,知道最高等级的父类加载器,如果父类加载器可以完成加载任务就成功返回,如果无法完成加载任务,就下放给下一级的子类,依次类推。如果所有的加载器都找不到的时候,就会抛出异常了,一般是ClassNotFoundException或NoClassDefException
      • 双亲委托机制是为了保证Java核心类的类型安全,比如你重写一个Objectl类了,但是Java的类加载机制将会是你的Object类无法生效。
    • 双亲委托机制是代理模式的一种
      • 并非所有的类加载器都采用双亲委托机制
      • tomcat服务器类加载器也使用代理模式,不同点在于它是首先尝试去加载某个类,如果找不到再代理给父类加载器。

    Java虚拟机如何判定两个Java类是相同的

    • 类的全限定类名是否相同
    • 加载此类的类加载器是否一样

    满足上述条件,才会认为类是相同的。即便是同样的字节码文件,被不同的类加载器加载,也会被认为是不同的Java类。

    代码清单2:测试判定Java类是否相同的类:Sample.java


    public class Sample {
        
        private Sample instance;
        
        public void setSample(Object instance) {
            this.instance = (Sample) instance;
        }
    
    }

    代码清单3:测试代码

  • 相关阅读:
    Scala实验二——3.统计学生成绩
    Scala实验二——2.模拟图形绘制
    Scala实验二——1.计算级数
    Scala类和对象笔记
    《能力陷阱》反思
    spark学习
    Ajax获取后台Servlet数据
    在进行pip安装时报错:Could not fetch URL https://pypi.org/simple/xrld/: There was a problem confirming the ssl certificate:
    pycharm连接服务器的内部环境时出错,输入用户密码之后报错:Permission denied, please try again authentication failed
    pycharm连接docker服务器遇到的错误:java.net.ConnectException: Connection refused
  • 原文地址:https://www.cnblogs.com/homeword/p/7502823.html
Copyright © 2020-2023  润新知