参考资料:
《深入理解Java虚拟机》 -周志明
Android中的动态加载机制
本篇不深入涉及Java类加载器,如果想更深入了解,可以看一下这篇博客http://blog.csdn.net/zhoudaxia/article/details/35824249
前言:动态加载在应用开发中有着很重要的地位,当我们项目越来越大,我们可以通过插件化来减少应用的内存,然后动态加载那些插件。还有一个方面,如果我们的应用频繁的更新,频繁的发布新版本,肯定会造成用户体验下降,那么我们可以用动态加载技术在不发布新版本的情况下更新一些模块。
那么既然要用动态加载,就肯定涉及到类加载器,我们先看一下Java中的类加载器。
一、Java中类加载器
在这里,我们不去说类加载的具体过程,只是总结一下Java中类加载器与双亲委派模型。
1.类加载器与类本身确定类的唯一性
对于一个类,这个类本身和加载它的类加载器共同确定其在虚拟机中的唯一性。
我们使用两个类加载器进行加载同一个类,那么这两个类是不相等的,那么虚拟机中会存在两个同名的类。
2.Java三种预定义类型类加载器
启动类加载器(Bootstrap ClassLoader,也称为引导类加载器)
该类加载器负责将存放在\lib目录中,或者被-Xbootclasspath参数所指定的路径中的,并且是虚拟机识别的(这点很重要)类加载到虚拟机中。
加载虚拟机识别的这一点与双亲委派模型配合很重要。在下面介绍,先留意这一点。
对于启动类加载器还有一点需要注意,也是和双亲委派模型有关,如果我们自定义一个类加载器,想把一个加载请求委派给启动类加载器,只需要使用null替代即可(可以看下面的loadClass()方法中的代码实现)。
开发者不可以直接使用该加载器。
扩展类加载器(Extension ClassLoader)
该加载器由sun.misc.Launcher$ExtClassLoader实现,负责加载\lib\ext目录中的,或者被java.ext.dirs系统变量所指定的路程中的所有类库。
开发者可以直接使用该加载器。
应用程序类加载器(Application ClassLoader,也称为系统类加载器)
该类加载器由sun.misc.Launcher$AppClassLoader实现。由于这个类加载器是ClassLoader中的getSystemClassLoader()方法的返回值,所以也称为系统类加载器。该加载器负责加载用户类路径(ClassPath)上所指定的类库。
开发者可以直接使用这个类加载器。如果应用程序中没有自定义过自己的类加载器,一般情况下该加载器是程序中的默认的类加载器。
3.双亲委派模型
上图显示了类加载器之间的层次关系,被称为类加载器的双亲委派模型。
除了顶层的启动类加载器外,其余的类加载器都应当有自己的父类加载器。
那么根据上图,如果一个类加载器收到了类加载的请求,它首先不会自己尝试加载这个类,而是把请求委派给父类加载器去加载,每一个层次的类加载器都是这样,那么所有的请求其实最后都是会到启动类加载器中,如果父类加载器反馈无法加载,子加载器才会尝试自己加载。
实现双亲委派模型的代码在loadClass()方法中:
protected Class<?> loadClass(String className, boolean resolve) throws ClassNotFoundException {
//检查请求加载的类是否已经加载过了
Class<?> c = findLoadedClass(className);
//如果没有,进行以下步骤
if (clazz == null) {
try {
//尝试使用父类加载器加载,父类加载器不为空,则使用父类加载器尝试加载
if(parent != null){
c = parent.loadClass(className, false);
}
//如果父类加载器为null,则使用启动类加载器作为父加载器,关于这一点,参考上面启动类加载器的介绍
else{
c = findBootstrapClassOrNull(name);
}
} catch (ClassNotFoundException e) {
// 如果父类加载器抛出异常,说明父类加载器无法完成加载请求
}
if (c == null) {
//父类无法完成加载时调用本身的findClass方法来进行类加载