ClassLoader介绍
类加载器是负责加载类的对象。ClassLoader 类是一个抽象类。如果给定类的二进制名称,那么类加载器会试图查找或生成构成类定义的数据。一般策略是将名称转换为某个文件名,然后从文件系统读取该名称的“类文件”。
每个 Class 对象都包含一个对定义它的 ClassLoader 的引用。
数组类的 Class 对象不是由类加载器创建的,而是由 Java 运行时根据需要自动创建。数组类的类加载器由 Class.getClassLoader() 返回,该加载器与其元素类型的类加载器是相同的;如果该元素类型是基本类型,则该数组类没有类加载器。
ClassLoader 类试用委托机制来加载类,每个ClassLoader实例都有一个父类加载器。
当需要查找类或者资源时ClassLoader实例会将任务委托给自己的父类加载器去处理。
虚拟机内置的类加载器(称为 “bootstrap class loader”)本身没有父类加载器,但是可以将它用作 ClassLoader 实例的父类加载器。
网络类加载器子类必须定义方法 findClass 和 loadClassData,以实现从网络加载类。下载组成该类的字节后,它应该使用方法 defineClass 来创建类实例。代码如下
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 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
. . .
}
}
>
ClassLoader.loadClass方法源码
1 | protected Class<?> loadClass(String name, boolean resolve) |
Class<?> c = findLoadedClass(name);
首先检查类是否已加载c = parent.loadClass(name, false);
如果没有被加载委托父类加载器加载,父类加载器执行同样的代码委托它的父类加载器加载直到没有父类加载器为止。c = findBootstrapClassOrNull(name);
调用虚拟机的类加载器c = findClass(name);
如果经过以上的委托机制还未加载到类调用自己本身的findClass方法加载类。- findClass方法是protected的 本身没有任何实现代码,而是直接抛出ClassNotFoundException异常,所以这个方法就是为了让我们自己实现类加载器时重写的,而且必须重写。
自定义类加载器
1 | /** |
test1测试我们可以通过使用自己的类加载器加载类
test2测试
根据打印的类加载器。发现一共有3个类加载MyClassLoader1、AppClassLoader、ExtClassLoader、其实还有一个BootstrapClassLoader(在没有父classLoader时使用)当loader2没有父类加载器时就会委托BootstrapClassLoader加载。
- 启动类加载器(Bootstrap ClassLoader):负责加载 JAVA_HOME\lib 目录中的,或通过-Xbootclasspath参数指定路径中的,且被虚拟机认可(按文件名识别,如rt.jar)的类
- 扩展类加载器(Extension ClassLoader):负责加载 JAVA_HOME\lib\ext 目录中的,或通过java.ext.dirs系统变量指定路径中的类库。
- 应用程序类加载器(Application ClassLoader):负责加载用户路径(classpath)上的类库。
总结
- 自定义类加载必须实现findClass方法
- Java装载类使用“全盘负责委托机制”。
“全盘负责”是指当一个ClassLoder装载一个类时,除非显示的使用另外一个ClassLoder,该类所依赖及引用的类也由这个ClassLoder载入;
“委托机制”是指先委托父类装载器寻找目标类,只有在找不到的情况下才从自己的类路径中查找并装载目标类。- 这一点是从安全方面考虑的,试想如果一个人写了一个恶意的基础类(如java.lang.String)并加载到JVM将会引起严重的后果,但有了全盘负责制,java.lang.String永远是由根装载器来装载,避免以上情况发生 除了JVM默认的三个ClassLoder以外,第三方可以编写自己的类装载器,以实现一些特殊的需求。
- 类文件被装载解析后,在JVM中都有一个对应的java.lang.Class对象,提供了类结构信息的描述。数组,枚举及基本数据类型,甚至void都拥有对应的Class对象。Class类没有public的构造方法,Class对象是在装载类时由JVM通过调用类装载器中的defineClass()方法自动构造的。