首先看整个大流程
一、加载(这是 ClassLoader 要做的事情)
ClassLoader 执行流程**
双亲模型
- 这是默认的 class 加载流程
- 一般我们写的代码都会在 AppClassLoader 中加载,Custom ClassLoader 是自定义 ClassLoader,后面在非双亲模型下大多是用这种方式加载,例如下面的代码:
public class Boy {
public void getGirl() {
Girl girl = new Girl();
girl.sayHello();
}
}
public class Girl {
public void sayHello() {
ClassLoader classLoader = this.getClass().getClassLoader();
System.out.println("hello I'm in " + classLoader.getClass().getSimpleName());
}
}
public class Test {
public static void main(String[] args) {
Boy boy = new Boy();
boy.getGirl();
}
}
// 输出
hello I'm in AppClassLoader
- 如果 底部的 ClassLoader 找不到需要的 class,会一直往上询问相应的 ClassLoader 是否有需要的 class,如果去到 Bootstrap ClassLoader 都没有,就会从 Bootstrap ClassLoader 一直往下,在对应的 class “管辖” 目录内尝试加载 class,如果一直都没有,就会抛出异常
- 也可以手动设置使用哪个加载器加载 class
- 同理如果要在 Bootstrap ClassLoader 加载 Girl 类的话在启动参数里面添加 -Xbootclasspath/a:D:/testClassLoader,其中 “D:/testClassLoader” 如下图,但是如果要获取到 Bootstrap ClassLoader 是不可能的,值为 null,因为 Bootstrap ClassLoader 是用 C++ 编写的,逻辑上并不存在 Bootstrap ClassLoader 的实体类
- 用自定义 ClassLoader 加载
public class MyClassLoader extends ClassLoader {
@Override
public Class<?> loadClass(String name) throws ClassNotFoundException {
try {
String clazzName = name.substring(name.lastIndexOf(".") + 1) + ".class";
InputStream is = getClass().getResourceAsStream(clazzName);
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);
}
}
}
-
关于 ContextClassLoader (上下文 ClassLoader):作用是可以在不同 ClassLoader 之间传递 ClassLoader,可以通过 Thread.currentThread().getContextClassLoader(); 来获取,通过 Thread.currentThread().setContextClassLoader(cl); 来设置一个 ClassLoader 进去,至于用途嘛,看实际的需要了
-
关于 class 的读取,可以通过文件、字符串、网络流等任何形式,但是最终都是以二进制流的形式加载
非双亲模型
- Tomcat 的 WebAppClassLoader
- OSGI 的 ClassLoader
二、连接
- 验证:对文件格式、字节码、元数据、符号引用进行验证,尽可能保证 class 是正确可执行的
- 准备:内存分配、元数据区内对象属性初始赋值,例如 public static value = 1;这时 value 会赋值为 0;对于 final 会赋值为 1;
- 解析:符号引用解析为直接引用,例如:字符串的引用
三、初始化
- static 赋值 和 static{} 语句执行
- 子类和父类的先后初始化
最后,还有很多细节未能一一道明,还要不断努力,加油!