分析Java的类加载器与ClassLoader(一):概述

来源:互联网 发布:2017云计算大会 ppt 编辑:程序博客网 时间:2024/06/05 19:56


有关类加载器的基本知识,请先看文章:http://blog.csdn.net/tangyangguang/article/details/8878302



1,类加载器概述
        Java程序是由许多独立的类字节码文件(*.class)组成。一个应用程序可能直接或间接的涉及到成千上万个Class,如果一次性全部装入内存,无疑是时间和空间的浪费。所以JVM启动时只会加载核心的类字节码和必要的其他类字节码,用户自定义的类将会在第一次使用时加载,这叫做动态加载类字节码。而类加载器就是用来加载类字节码文件到JVM中,以供程序使用的。

2,JVM中的自带的类加载器
  • Bootstrap ClassLoader
    • 启动类加载器,用c/c++实现的,它负责加载核心Class(即所有java.*开头的类)。
    • 这个类加载器没有对应的Java类。
  • Extension ClassLoader
    • 扩展类加载器,它负责加载扩展的Class(存放在JRE的lib/ext/目录下的类)。
    • 这个类加载器对应sun.misc.Launcher$AppClassLoader类。
  • Application ClassLoader
    • 应用程序类加载器,负责加载应用程序自身的类(CLASSPATH目录中的Class)。
    • 这个类加载器对应sun.misc.Launcher$ExtClassLoader类。

Bootstrap ClassLoader是最顶级的类加载器,也是Extension ClassLoader的父“类加载器”,Extension ClassLoader是Application ClassLoader的父“类加载器”。如下图所示:

    


测试代码:
package cn.itcast;public class ClassLoaderTest {public static void main(String[] args) {ClassLoader cl = Thread.currentThread().getContextClassLoader();System.out.println("当前的类加载器:" + cl);while(cl != null){cl = cl.getParent();System.out.println("parent类加载器:" + cl);}}}

执行结果:

当前的类加载器:sun.misc.Launcher$AppClassLoader@19821fparent类加载器:sun.misc.Launcher$ExtClassLoader@addbf1parent类加载器:nul


3,“ClassLoader”与“类加载器”的区别
       我们平常说的ClassLoader实际上表示两个概念:一是“类加载器”概念;二是ClassLoader类,全名是java.lang.ClassLoader。java.lang.ClassLoader是JDK中的一个普通类(ClassLoader是一个抽象类),是用Java语言编写的,我们可以自己写一个ClassLoader的子类,以扩展Java虚拟机加载类的方式。 


4,类加载器使用委托模式进行类加载
        类加载器使用委托模型来搜索类和资源。每个“类加载器”实例都有一个相关的父“类加载器”(parent)。需要查找类或资源时,类加载器实例会先委托给其父类加载器进行加载,如果parent是null的话,则表示为Bootstrap类加载器,只有在上级类加载器没有找到的情况下,自己才会加载,这就避免我们重写一些系统类,来破坏系统的安全。

分析java.lang.ClassLoader的源码:
protected synchronized Class loadClass(String name, boolean resolve) throws ClassNotFoundException {// 先从缓存中拿Class:// 1, 如果这个Class之前被加载过,就会记录到缓存中,这里就会返回缓存的Class对象。// 2, 如果这个Class之前没有被加载过,则这里返回nullClass c = findLoadedClass(name);if (c == null) {// 如果缓存中没有想要的Class对象,则进行加载try {// 如果parent不为null,则调用parent的loadClass进行加载if (parent != null) {c = parent.loadClass(name, false);}// 如果parent为null,则调用BootstrapClassLoader进行加载else {c = findBootstrapClass0(name);}} catch (ClassNotFoundException e) {// 如果仍然无法加载成功,则调用自身的findClass进行加载c = findClass(name);}}if (resolve) {resolveClass(c);}return c;}

从以上源码中可以得知:
1,类加载器使用委托模式进行类加载。
2,我们如果想自己写一个类加载器,则只需要重载findClass()这个方法。


5,规则细节

  1. 类加载器使用委托模式进行类加载。
  2. Class.getClassLoader()方法可以返回加载这个Class的ClassLoader,如果返回null,表示是Bootstrap ClassLoader。
  3. 如果类A引用了类B,不管是直接引用还是用Class.forName()引用,JVM就会找到加载类A的ClassLoader,并用这个ClassLoader来加载类B。
  1. 我们如果想自己写一个类加载器,则只需要重载findClass()这个方法。
  2. 在自定义的ClassLoader对象中,需要指定一个父对象;如果没有指定的话,系统自动指定ClassLoader.getSystemClassLoader()为父对象。
  3. 我们可以在自定义的ClassLoader中重写loadClass()方法,在实现代码中不采用委托模式进行类加载,但不推荐这样做。
  1. Class.forName()是加载类字节码并进行初始化(例如执行static代码块),如果之前加载过,则会使用缓存的Class。
  2. ClassLoader.loadClass()是加载类字节码,但不会进行初始化(例如不会执行static代码块),而是在第一次使用这个类时执行初始化。
  3. 如果使用同一个ClassLoader实例多次加载同一个Class(自己重写loadClass()方法可以实现这个效果),会抛java.lang.LinkageError错误。

        (实验代码将写到后面的文章中)