疯狂java


您现在的位置: 疯狂软件 >> 新闻资讯 >> 正文

分析Java的类加载器与ClassLoader


 

        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的父“类加载器”。

  测试代码:

  [java] view plaincopy

  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);

  }

  }

  }

  执行结果:

  [plain] view plaincopy

  当前的类加载器:sun.misc.Launcher$AppClassLoader@19821f

  parent类加载器:sun.misc.Launcher$ExtClassLoader@addbf1

  parent类加载器:nul

  3,“ClassLoader”与“类加载器”的区别

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

  4,类加载器使用委托模式进行类加载

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

  分析java.lang.ClassLoader的源码:

  [java] view plaincopy

  protected synchronized Class loadClass(String name, boolean resolve) throws ClassNotFoundException {

  // 先从缓存中拿Class:

  // 1, 如果这个Class之前被加载过,就会记录到缓存中,这里就会返回缓存的Class对象。

  // 2, 如果这个Class之前没有被加载过,则这里返回null

  Class 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,规则细节

  类加载器使用委托模式进行类加载。

  Class.getClassLoader()方法可以返回加载这个Class的ClassLoader,如果返回null,表示是Bootstrap ClassLoader。

  如果类A引用了类B,不管是直接引用还是用Class.forName()引用,JVM就会找到加载类A的ClassLoader,并用这个ClassLoader来加载类B。

  我们如果想自己写一个类加载器,则只需要重载findClass()这个方法。

  在自定义的ClassLoader对象中,需要指定一个父对象;如果没有指定的话,系统自动指定ClassLoader.getSystemClassLoader()为父对象。

  我们可以在自定义的ClassLoader中重写loadClass()方法,在实现代码中不采用委托模式进行类加载,但不推荐这样做。

  Class.forName()是加载类字节码并进行初始化(例如执行static代码块),如果之前加载过,则会使用缓存的Class。

  ClassLoader.loadClass()是加载类字节码,但不会进行初始化(例如不会执行static代码块),而是在第一次使用这个类时执行初始化。

  如果使用同一个ClassLoader实例多次加载同一个Class(自己重写loadClass()方法可以实现这个效果),会抛java.lang.LinkageError错误。