zoukankan      html  css  js  c++  java
  • 类加载器ClassLoader的理解

      最近在做一个热加载Class的小组件,这个组件需要对类加载器ClassLoader有所了解,我就顺便借这个机会把学到的一点皮毛与大家分享一下。

     从Class文件开始

    ClassLoader,顾名思义就是类加载器。简单的说就是把Class文件加载到JVM中,之后程序就能正常的运行了。

    我们平时写的代码都是.java格式的文件,但是这个文件是不能够直接运行的。比如下面这个简单的测试程序Test.java

    1 public class Test {
    2 
    3     public static void main(String[] args) {
    4         System.out.println("Testing");
    5     }
    6 }

    在文件夹下看,就是这么一个文件:

    我们在命令行下编译一下这个Java文件:

    1 javac Test.java 

    然后就可以在目录下看到生成的class文件,也就是JVM可以识别运行的文件。

    Java自带的类加载器

    Java自带有三个类加载器:

    Bootstrap ClassLoader:主要加载核心类库,%Java_HOME%lib 下的rt.jar、resources.jar、charsets.jar和class等,总之就是jdk安装目录下lib目录下的一些核心jar包。

    Extention ClassLoader 扩展的类加载器,主要加载目录%JRE_HOME%libext目录下的jar包和class文件,就是JRE安装目录下lib目录下ext目录下的jar和class文件。

    Appclass Loader也称为SystemAppClass应用程序加载器 加载当前应用的classpath的所有类,这个就是很好理解了,就是我们平时自己写的项目类路径下的类。

    双亲委派机制

    接下来我先祭出一张常见的图:

    双亲委派机制的意思是:一个类加载器遇到一个加载类的请求,首先不会自己去加载,而是看自己的父加载器能否加载这个类,然后一级一级往上传直到遇到能够加载这个类的加载器。

    简单可以形容为一家几口吃苹果:
    小明:爸爸,这苹果你吃吗?
    小明的爸爸:我先问问你爷爷,爸,这苹果你吃吗?
    小明的爷爷:你们真孝顺,那我就恭敬不如从命啦。

    如果遇到顶层的父类加载器无法加载,该怎么办?这个时候,就往下找,找到第一个能加载这个类的加载器
    小明:爸爸,这苹果你吃吗?
    小明的爸爸:我先问问你爷爷,爸,这苹果你吃吗?
    小明的爷爷:牙不好啦,我不吃啦你们吃吧。
    小明的爸爸:好的,我的牙口还不错,那我就吃了。

    那么为什么需要一个机制呢,原因主要是为了避免类加载程序的混乱。

    比如Java官方指定了一个java.lang.String类,然后你自己又重新写了一个java.lang.String类的类。这个时候,你想import一个String类型,就没有办法判断到底这个String是哪个String了。

    当然这边要多说一句,父类加载器不等于父类,也就是上面的三个类加载器并不是继承关系。

    自定义ClassLoader

    刚刚那张图大家应该已经发现了,除了三个系统自带的ClassLoader以外,最底层还有几个自定义的类加载器。

    没错,原来的类加载器只是按照特定的方式加载指定目录下的jar包,那么如果你想按照自己的要求加载一些东西呢?这就需要你自己去定义一个classloader了。

    比如以我为例,我希望可以多次重新加载同一个Class。

    ClassLoader中,加载Class的方法是loadClass(),我们可以看一下源码:

     1    protected Class<?> loadClass(String name, boolean resolve)
     2         throws ClassNotFoundException
     3     {
     4         synchronized (getClassLoadingLock(name)) {
     5             // First, check if the class has already been loaded
     6             Class<?> c = findLoadedClass(name);
     7             if (c == null) {
     8                 long t0 = System.nanoTime();
     9                 try {
    10                     if (parent != null) {
    11                         c = parent.loadClass(name, false);
    12                     } else {
    13                         c = findBootstrapClassOrNull(name);
    14                     }
    15                 } catch (ClassNotFoundException e) {
    16                     // ClassNotFoundException thrown if class not found
    17                     // from the non-null parent class loader
    18                 }
    19 
    20                 if (c == null) {
    21                     // If still not found, then invoke findClass in order
    22                     // to find the class.
    23                     long t1 = System.nanoTime();
    24                     c = findClass(name);
    25 
    26                     // this is the defining class loader; record the stats
    27                     sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
    28                     sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
    29                     sun.misc.PerfCounter.getFindClasses().increment();
    30                 }
    31             }
    32             if (resolve) {
    33                 resolveClass(c);
    34             }
    35             return c;
    36         }
    37     }

    可是在ClassLoader中的加载class的方法loadClass()中,有一个  findLoadedClass() 方法来判断Class是否已经被加载的操作,不符合要求,而且loadClass()方法中也会按照双亲委派去找寻父类加载器,不符合要求。(看不懂源码没有关系,里面的英文还是可以看懂的嘛哈哈)

    于是我的步骤

    1. 编写一个类继承自ClassLoader抽象类。 
    2. 复写它的findClass()方法,直接调用findClass()来找寻Class文件,而不是loadClass()。
    3. findClass()方法中调用defineClass(),defineClass()将class二进制内容转换成Class对象并加载进内存。 

    这样子,就实现了我的重复加载Class的目的。

  • 相关阅读:
    搞笑汉文化
    學習.Net(c#)打印打印結構
    學習.Net(c#)打印調用打印界面
    在Windows下Svn架設總結
    OpenFileDialog、SaveFileDialog常用屬性、對話框用法及得到系統特殊文件夾路徑
    c# FontDialog、ColorDialog、FolderBrowserDialog常用屬性
    學習.Net(c#)打印頁面設置
    學習.Net(c#)打印多頁打印
    學習.Net(c#)打印打印預覽
    C# 記錄程序運行時間
  • 原文地址:https://www.cnblogs.com/jing-daye/p/6647233.html
Copyright © 2011-2022 走看看