JVM设计团队把类加载阶段中“通过一个类的全限定名来获取此类的二进制字节流”这个动作放到java虚拟机外部去实现,以便让应用程序自己决定如何去获取所需要的类。实现这个动作的代码模块成为“类加载器”。
1.类与类加载器
对于任一个类,都需要由加载它的类加载器和这个类本身一同确立其在Java虚拟机中的唯一性,每一个类加载器都拥有一个独立的类名称空间。
所指的唯一性可判断两个类是否“相等”,包括代表类的Class对象的equals()方法、isAssignableFrom()方法、isInstance()方法的返回结果,也包括使用instanceof关系作对象所属关系判定的情况。如下代码演示了不同类加载器对instanceof关键字运算的结果的影响。
package ClassLoader;
import java.io.*;
/****
*类加载器与instanceof关键字演示
*@author iamwiam
*/
public class ClassLoaderTest{
public static void main(String[] args) throws InstantiationException, IllegalAccessException, ClassNotFoundException{
ClassLoader myClassLoader = new ClassLoader(){
public Class<?> loadClass(String name) throws ClassNotFoundException{
try{
String fileName = name.substring(name.lastIndexOf(".")+1)+".class";
InputStream is = getClass().getResourceAsStream(fileName);
if(is==null)
return super.loadClass(name);
byte[] b = new Byte[is.available()];
is.read(b);
return defineClass(name,b,0,b.length);
}catch(IOException e){
throw new ClassNotFoundException(name);
}
}
};
Object obj = myClassLoader.loadClass("ClassLoader.ClassLoaderTest").newIntance();
Syso(obj.getClass());
Syso(obj instanceof ClassLoaderTest);
}
}
结果
2.双亲委派模型
1)从JVM角度讲,只存在两种不同的类加载器:一种是启动类加载器(Bootstrap ClassLoader),这个类加载器使用C++语言实现,是虚拟机自身的一部分;另一种就是所有其他的类加载器,这些加载器由java语言实现,独立于虚拟机外,并且全都继承自抽象类java.lang.ClassLoader。
2)从开发人员角度,绝大部分java程序都会使用到以下三种系统提供的类加载器。
1>启动类加载器(Bootstrap ClassLoader)
负责将存放在< JAVA_HOME>\lib目录中的,或者被-Xbootcalsspath参数所指定的路径中,并且虚拟机识别的类库加载到虚拟机内存中。启动类加载器无法被java程序直接引用,用户在编写自定义的类加载器时,如果需要把加载请求委派给引导类加载器,那直接使用null即可。如下为java.lang.classLoader.getClassLoader()方法的代码片段。
public classLoader getClassLoader(){
ClassLoader cl = getClassLoader0();
if(cl==null)
return null;
SecurityManager sm = System.GetSecurityManager();
if(sm!=null){
ClassLoader ccl = ClassLoader.getCallerClassLoader();
if(ccl!=null&&ccl!cl&&cl.isAncestor(ccl)){
sm.checkPermission(SecurityConstants.GET_CLASSLOADER_PERMISSION);
}
}
return cl;
}
2>扩展类加载器(Extension ClassLoader)
这个加载器由sun.misc.Launcher$ExtClassLoader实现,它负责加载< JAVA _ HOME >\lib\ext目录中的,或者被java.ext.dirs系统变量所指定的路径中的所有类库,开发者可以直接使用扩展类加载器。
3>应用程序加载器(Application ClassLoader)
这个类加载器由sun.misc.Launcher$AppClassLoader实现。由于这个类加载器是ClassLoader中的getSystemClassLoader()方法的返回值,所以一般也称它为系统类加载器。它负责加载用户类路径(ClassPath)上所指定的类库,开发者可以直接使用这个类加载器,如果应用程序中没有自定义过自己的类加载器,一般情况下这个就是程序中默认的类加载器。我们的应用程序都是由这3中类加载器相互配合完成,三者关系如下:
双亲委派模型
双亲委派模型的工作过程:如果一个类加载器收到了类加载的请求,它首先把这个请求委派给父类加载器完成,每一个层次的类加载器都是如此,因此所有的加载请求最终都应传送到顶层的启动类加载器中,只有当父类加载器反馈自己无法完成这个加载请求时,子加载器才会自己完成请求。
实现双亲委派模型代码的逻辑是:先检查是否已经被加载过,如没有则调用父类加载器的LoadClass()方法,若父类加载器为空,则默认使用启动类加载器作为父类加载器。如果父类加载失败,抛出ClassNotFoundException异常后,再调用自己的findClass()方法进行加载。实现代码如下:
protected synchronized Class<?> loadClass(String name,boolean resolve)throws ClassNotFoundException{
//check the class has been loaded or not
Class c = findLoadedClass(name);
if(c == null){
try{
if(parent != null){
c = parent.loadClass(name,false);
}else{
c = findBootstrapClassOrNull(name);
}
}catch(ClassNotFoundException e){
//if throws the exception ,the father can not complete the load
}
if(c == null){
c = findClass(name);
}
}
if(resolve){
resolveClass(c);
}
return c;
}
注:参考《深入理解Java虚拟机》周志明著