ClassLoader做什么的?
顾名思义,它是用来加载Class的。它负责将Class的字节码形式转换成内存形式的Class对象。字节码可以来自磁盘文件*.class,也可以是jar包里的*.class,也可以来自远程服务器提供的字节流,字节码的本质就是一个字节数组byte[]。
延迟加载
JVM允许并不是一次性加载所有需要的类,它是按需加载,也就是延迟加载。程序在运行的过程中会逐渐遇到很多不认识的新类,这时候就会调用ClassLoader来加载这些类。加载完成后会将Class对象存在ClassLoader里面,下次就不需要重新加载了。
分类
JVM运行实例中会存在多个ClassLoader,不同的ClassLoader会从不同的地方加载字节码文件。这些地方可以是文件目录,可以是jar文件,可以是网络地址。JVM内置了三个重要的ClassLoader,分别是BootstrapClassloader、ExtensionClassLoader和AppClassLoader。
BootstrapClassLoader又称为根加载器,负责加载JVM运行时的核心类,这些类位于JAVA_HOME/lib/rt.jar文件中,比如java.util.*、java.io.*、java.lang.*等等;
ExtensionClassLoader负责加载JVM扩展类,它们位于JAVA_HOME/lib/ext/*.jar中;
AppClassLoader才是直接面向用户的加载器,它会加载Classpath环境变量里定义的路径中的jar包和目录。我们自己编写的代码以及使用的第三方jar包,通常都是由它加载的。
在Android虚拟机中,ClassLoader有以下类型:
BootClassLoader,和Java中的BootstrapClassLoader作用类似,是用来加载Framework层的字节码文件的;
PathClassLoader,和Java中的AppClassLoader作用类似,用来加载已经安装到系统中的APK文件中的Class文件;
DexClassLoader,和Java中的CustomClassLoader作用类似,用来加载指定目录中的字节码文件;
BaseDexClassLoader,这是一个父类,DexClassLoader和PathClassLoader都是它的子类。
双亲委派
双亲委派是指ClassLoader在加载一个未知的类名时,它不是立即去搜寻Classpath,而是会先将这个类名称交给父加载器去加载,只有当父加载器不能加载这个类时,才会自己去加载。这里说的父加载器,但是实际上他们并不是继承的关系,而是组合关系。当ClassLoader的parent的值是null时,表示它的父加载器是根加载器,如果某个Class对象的classLoader属性值是null,表示这个类是由根加载器加载的。
自定义加载器
ClassLoader里面有三个重要的方法。loadClass()、findClass()和defineClass()。
loadClass()方法是加载目标类的入口,它首先会查找当前ClassLoader以及它的父类加载器里面是否已经加载了目标类,如果没有找到就会让父类加载器尝试加载,如果父类加载器都加载不了,就会调用fingClass()让自定义加载器自己加载目标类。ClassLoader的findClass()方法是需要子类来覆盖的,不同的加载器将使用不同的逻辑来获取目标类的字节码。拿到这个字节码之后再调用defineClass()方法将字节码转换成Class对象。伪代码如下:
class ClassLoader {
// 加载入口,定义了双亲委派规则
Class loadClass(String name) {
// 是否已经加载了
Class t = this.findFromLoaded(name);
if(t == null) {
// 交给双亲
t = this.parent.loadClass(name)
}
if(t == null) {
// 双亲都不行,只能靠自己了
t = this.findClass(name);
}
return t;
}
// 交给子类自己去实现
Class findClass(String name) {
throw ClassNotFoundException();
}
// 组装Class对象
Class defineClass(byte[] code, String name) {
return buildClassFromCode(code, name);
}
}
class CustomClassLoader extends ClassLoader {
Class findClass(String name) {
// 寻找字节码
byte[] code = findCodeFromSomewhere(name);
// 组装Class对象
return this.defineClass(code, name);
}
}