文章摘抄至 http://longdick.iteye.com/blog/442213/
要深入了解ClassLoader,首先就要知道ClassLoader是用来干什么的,顾名思义,它就是用来加载Class文件到JVM,以供程序使用的。我们知道,java程序可以动态加载类定义,而这个动态加载的机制就是通过ClassLoader来实现的,所以可想而知ClassLoader的重要性如何。
看到这里,可能有的朋友会想到一个问题,那就是既然ClassLoader是用来加载类到JVM中的,那么ClassLoader又是如何被加载呢?难道它不是java的类?
没有错,在这里确实有一个ClassLoader不是用java语言所编写的,而是JVM实现的一部分,这个ClassLoader就是bootstrap classloader(启动类加载器),这个ClassLoader在JVM运行的时候加载java核心的API以满足java程序最基本的需求,其中就包括用户定义的ClassLoader,这里所谓的用户定义是指通过java程序实现的ClassLoader,一个是ExtClassLoader,这个ClassLoader是用来加载java的扩展API的,也就是/lib/ext中的类,一个是AppClassLoader,这个ClassLoader是用来加载用户机器上CLASSPATH设置目录中的Class的,通常在没有指定ClassLoader的情况下,程序员自定义的类就由该ClassLoader进行加载。
当运行一个程序的时候,JVM启动,运行bootstrap classloader,该ClassLoader加载java核心API(ExtClassLoader和AppClassLoader也在此时被加载),然后调用ExtClassLoader加载扩展API,最后AppClassLoader加载CLASSPATH目录下定义的Class,这就是一个程序最基本的加载流程
java应用环境中不同的class分别由不同的ClassLoader负责加载。
一个jvm中默认的classloader有Bootstrap ClassLoader、Extension ClassLoader、App ClassLoader,分别各司其职:
- Bootstrap ClassLoader 负责加载java基础类,主要是 %JRE_HOME/lib/ 目录下的rt.jar、resources.jar、charsets.jar和class等
- Extension ClassLoader 负责加载java扩展类,主要是 %JRE_HOME/lib/ext 目录下的jar和class
- App ClassLoader 负责加载当前java应用的classpath中的所有类。
其中Bootstrap ClassLoader是JVM级别的,由C++撰写;Extension ClassLoader、App ClassLoader都是java类,都继承自URLClassLoader超类。
Bootstrap ClassLoader由JVM启动,然后初始化sun.misc.Launcher ,sun.misc.Launcher初始化Extension ClassLoader、App ClassLoader。
下图是ClassLoader的加载类流程图,以加载一个类的过程类示例说明整个ClassLoader的过程。
public class ClassLoaderTree { public static void main(String[] args) { ClassLoader loader = ClassLoaderTree.class.getClassLoader(); /** sun.misc.Launcher$AppClassLoader@39ab89 系统类加载器 sun.misc.Launcher$ExtClassLoader@2cb49d 扩展类加载器 */ while(loader != null){ System.out.println(loader.toString()); loader = loader.getParent(); } } }
Bootstrap ClassLoader、Extension ClassLoader、App ClassLoader三者的关系如下:
Bootstrap ClassLoader是Extension ClassLoader的parent,Extension ClassLoader是App ClassLoader的parent。但是这并不是继承关系,只是语义上的定义,基本上,每一个ClassLoader实现,都有一个Parent ClassLoader。
可以通过ClassLoader的getParent方法得到当前ClassLoader的parent。Bootstrap ClassLoader比较特殊,因为它不是java class所以Extension ClassLoader的getParent方法返回的是NULL。
当使用ClassLoader类的loadClass()方法来加载某个类时,该方法只是加载该类,并不会执行该类的初始化。使用Class的forName()静态方法才会导致强制初始化该类。
package hb.reflect; public class Tester{ static{ System.out.println("初始化块"); } }
package hb.reflect; public class ClassLoaderTest { public static void main(String[] args) throws ClassNotFoundException { ClassLoader c1 = ClassLoader.getSystemClassLoader(); //不会执行初始化 c1.loadClass("hb.reflect.Tester"); System.out.println("系统加载Tester类"); //执行初始化内容 Class.forName("hb.reflect.Tester"); } }
打印内容:
系统加载Tester类
初始化块
如果将代码如下修改:
package hb.classloader; public class ClassLoaderTest { public static void main(String[] args) throws ClassNotFoundException { ClassLoader c1 = ClassLoader.getSystemClassLoader(); //不会执行初始化 Class clazz = c1.loadClass("hb.classloader.Tester"); // try { Tester tester = (Tester)clazz.newInstance(); tester.print("class.newInstance() is ok"); } catch (InstantiationException | IllegalAccessException e) { e.printStackTrace(); } System.out.println("系统加载Tester类"); //执行初始化内容 Class.forName("hb.classloader.Tester"); } }
打印结果:
初始化块
class.newInstance() is ok
系统加载Tester类