ClassLoader是经常出现又让很多人望而却步的词,所以我将试图用最浅显易懂的方式来说一下ClassLoader,希望对不了解该机制的朋友们能起到一点点的作用。
要深入了解ClassLoader,首先就要知道ClassLoader是用来干什么的,顾名思义,它就是用来加载Class文件到JVM(Java Virtual Machine(Java虚拟机)的缩写)。以供程序使用的。我们知道,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,这就是一个程序最基本的加载流程。
上面大概解释了一下ClassLoader的作用以及一个最基本的加载流程,下面来解释一下ClassLoader加载的方式,说到ClassLoader的加载方式,不得不解释一下ClassLoader在这里使用了双亲委托模式进行类加载。
每一个自定义ClassLoader都必须继承ClassLoader这个抽象类,而每个ClassLoader都会有一个parent ClassLoader,我们可以看一下ClassLoader这个抽象类中有一个getParent()方法,这个方法用来返回当前ClassLoader的parent,注意,这个parent不是指定的被继承的类,而是在实例化该ClassLoader时指定的一个ClassLoader,如果这个parent为null,那么就默认该ClassLoader的parent是bootstrap classloader,这个parent有什么用呢?
我们可以考虑这样一种情况,假设我们自定义了一个ClientDefClassLoader,我们使用这个自定义的ClassLoader加载java.lang.String,那么这里String是否会被这个ClassLoader加载呢?事实上,java.lang.String这个类并不是被这个ClientDefClassLoader加载,而是由bootstrap classloader进行加载,为什么会这样?实际上这就是双亲委托模式的原因,因为在任何自定义ClassLoader加载一个类之前,它都会先委托它的父亲ClassLoader进行加载,只有当父亲ClassLoader无法加载成功后,才会由自己加载,在上面这个例子里,因为java.lang.String是属于Java核心API的一个类,所以当使用ClientDefClassLoader加载它的时候,该ClassLoader会先委托他的父亲ClassLoader进行加载,上面说过,当ClassLoader的parent为null时,ClassLoader的parent就是bootstrap classloader,所以在ClassLoader的最顶层就是bootstrap classloader,因此最终委托到bootstrap classloader的时候,bootstrap classloader就会返回String的Class。
下面我们看一下ClassLoader中的一段源码:
public class Snippet{
protected synchronized Class loadClass(String name,boolean resolve) throws
ClassNotFoundException{
//首先检查该name指定的class是否被加载
Class c = findLoadedClass(name);
if(c==null){
try{
if(parent!=null){
//如果parent不为null,则调用parent的loadClass进行加载
c=parent.loadClass(name,false);
}else{
//如果parent为null,则调用BootstrapClassLoader进行加载
c=findBootstrapClass0(name);
}
}catch(ClassNotFoundException e){
//如果仍然无法加载成功,则调用自身的findClass进行加载
c=findClass(name);
}
}
if(resolve){resolveClass(c);}
return c;
}
}
从上面的一段代码,我们可以看出一个类加载的大概过程,与我们刚开始所举的例子是一样的,而我们要实现一个自定义类的时间,只需要实现findClass方法即可。