——Java培训、Android培训、iOS培训、.Net培训、期待与您交流! ——-
类加载器的作用
当我们编写出java文件时就有,经过java编译器的编写之后转回成class文件,当我们用到某一class类时,就要把该类的字节码加载进内存才可以用,而这一加载的过程就要通过类加载器完成,也就是说java类通过类加载器加载进内存。
类加载器的结构
Java 中的类加载器大致可以分成两类,一类是系统提供的,另外一类则是由 Java 应用开发人员自定义编写的。系统提供的类加载器主要有下面三个:
BootStrap:在java虚拟机启动的时候会利用这个类加载器来加载 JDK安装目录下的 /JRE/LIB/rt.jar 也就是系统默认导入的一些类例如System类,这个类加载器不是类 。只是作为一个java中类的起源工具,没有父类加载器。
ExpClassLoader:这个类加载器加载JDK安装目录的/JRE/LIB/ext目录中的类 我们只要把我们的类打包成JAR包放在这里即可。
AppClassLoader:我们在java程序中classpath对应的类都有这个AppClassLoader导入进来。
类加载器间的关系:
类加载器与java类有着类似的关系,每一个java类都有父类,而Object是所有java类的父类,自身没有父类。同Object是所有类的父类一样,BootStrap是所用加载器的父类,自身是根加载器,没有父类加载器,除了BootStrap不是java类以外,其他的类加载器都是java类,ExpClassLoader的父类加载器是BootStrap,AppClassLoader的父类加载器是ExpClassLoader,每一个加载器都有指定加载目录,自定义的加载器在编写时要指定父加载器或者用默认的父加载器。这样我们就可以知道类加载器是一个树状组织结构。
练习代码:
/*
* 打印GenericTest的加载器以及父类的以上的加载器
*/
public class GenericTest {
public static void main(String[] args) {
//获得本类的加载器
ClassLoader loader=GenericTest.class.getClassLoader();
//打印父类类以上的加载器
while(loader !=null)
{
System.out.println(loader);
loader = loader.getParent();
}
//打印根类加载器
// 因为根类加载器不是java类,所以为null
System.out.println(loader);
}
}
运行效果:
委托机制
某一个类一般会用当前线程的类加载器加载该类,也可以指定类加载器加载一个类,类加载器通过委托机制加载类,如A类指定B加载器加载,B不会马上加载该类,会把加载的任务交给父类加载器加载,同样父类加载器会把加载任务交给下一个父类加载器,直到根加载器,因为根加载器么有父类加载器,所用他会尝试加载A类,因为每一个加载器都会指定加载类的目录,当在根目录不存在A类时,根加载器加载失败,根加载器会把加载任务给他的子加载器,子加载器会尝试加载,如果能够加载,此次加载完成,如果还是加载失败,会把加载任务继续给下一个子加载器,直到加载成功,当所用的父加载器都无法加载时,B加载器会尝试加载,如果B加载器加载失败,那么此次的类加载失败,程序报错。这就是类加载器的委托机制。
那为什么要用委托机制,用委托机制的好处?
方便管理,如果A加载器与B加载器是C加载器的子加载器,当一个L类同时能够被三个加载器加载时,A加载器加载了L,因为B加载器不知道L已经加载,可能会再次对L进行加载,这样就类被多次加载,用委托机制,只能有C加载器能够加载L类,避免了类的多次多次加载,有效地管理了类的加载。
自定义类加载器
定义加载器要求继承ClassLoader 并重写findClass方法。
ClassLoader类部分方法:
defineClass(byte[] b, int off, int len)
将一个 byte 数组转换为 Class 类的实例。
findClass(String name):
使用指定的二进制名称查找类。
findLoadedClass(String name):
如果 Java 虚拟机已将此加载器记录为具有给定二进制名称的某个类的启动加载器,则返回该二进制名称的类。
自定义加载器利用了模板设计模式,findLoadedClass方法会找父类的加载器加载类,当加载不成功,会调用findClass方法加载class文件,把得到的二进制代码返回到defineClass方法中,该方法会把二进制代码转换成class的字节码加载进内存,所以我们只有重写findClass方法,通过自定义的方法读入class文件的二进制代码。
class NetworkClassLoader extends ClassLoader {
String host;
int port;
public Class findClass(String name) {
byte[] b = loadClassData(name);
return defineClass(name, b, 0, b.length);
}
private byte[] loadClassData(String name) {
// load the class data from the connection
. . .
}
}