类加载过程
- 加载:在磁盘通过IO读入字节码文件,
- 验证:验证字节码的正确性
- 准备:给类的静态变量分配内存,同时根据类型赋予初始默认值。
- 解析:将符号引用替换为直接引用。
- 初始化:给类的静态变量初始化为指定的值,执行静态代码块。
- 注意:final修改的类常量,在编译的时候就会赋值。实例的常量,是在创建对象的时候才赋
类加载器分类
- 引导类加载器(BootStrap ClassLoader):负载加载JRE/lib目录下的核心类库。(它比较特殊,底层是由C++实现的)
- 扩展类加载器(Extension ClassLoader):负载加载JRE/lib/ext目录下jar包中的类。
- 应用类加载器(Application ClassLoader):负载加载ClassPath路径下的自己写的类,也是系统的默认类加载器。
- 自定义类加载器:负载加载自定义路径下的类。
- 注意:ExtClassLoader和AppClassLoader是Launcher的静态内部类,在启动类Launcher初始化的过程这个,同时会实例化ExtClassLoader和AppClassLoader。
public Launcher() {
Launcher.ExtClassLoader var1;
try {
var1 = Launcher.ExtClassLoader.getExtClassLoader();
} catch (IOException var10) {
throw new InternalError("Could not create extension class loader", var10);
}
try {
this.loader = Launcher.AppClassLoader.getAppClassLoader(var1);
} catch (IOException var9) {
throw new InternalError("Could not create application class loader", var9);
}
Thread.currentThread().setContextClassLoader(this.loader);
String var2 = System.getProperty("java.security.manager");
if (var2 != null) {
SecurityManager var3 = null;
if (!"".equals(var2) && !"default".equals(var2)) {
try {
var3 = (SecurityManager)this.loader.loadClass(var2).newInstance();
} catch (IllegalAccessException var5) {
} catch (InstantiationException var6) {
} catch (ClassNotFoundException var7) {
} catch (ClassCastException var8) {
}
} else {
var3 = new SecurityManager();
}
if (var3 == null) {
throw new InternalError("Could not create SecurityManager: " + var2);
}
System.setSecurityManager(var3);
}
}
双亲委派机制
- 系统默认的类加载器是应用类加载器,一个类要加载,应用类加载器会判断之前有没有加载过当前类,如果没有加载过,就会向上委托,交给扩展类加载器来进行加载。
- 扩展类加载器加载当前类时,也会进行判断有没有加载过当前类,如果没有就继续向上委托,交给引导类加载器来进行加载。
- 引导类加载会先进行判断有没有加载过当前类,如果没有,就会从指定加载的类目录中去找当前类,如果找到,就由引导类加载器加载该类;如果没有找到,就说明加载失败,就向下交给扩展类加载器去加载该类。
- 扩展类加载器也会从指定加载的类目录中去找当前类,如果找到,就由扩展类加载器加载该类;如果没有找到,说明也加载失败,继续向下交给应用类加载器去加载该类。
- 应用类加载器会从ClassPath中去找当前类,如果找到,就有应用类加载器加载该类。
为什么要设计双亲委派机制?
- 避免类的重复加载:父加载器已经加载的类,子加载器无需加载,保证被加载类的唯一性。
- 沙箱安全机制:防止核心类库被恶意篡改。
思考:为什么类加载要从下向上委托,而不是直接从上向下委托?
因为被加载的类,绝大部分都是ClassPath路径下我们自己编写的类,采用从下向上委托,只有第一次加载需要走一次完成的委托流程,重复加载的时候应用类加载就可以直接加载;如果,采用从上向下委托的流程,我们编写的类每次加载都要走一遍委托的流程,整体来看,还是自下向上委托设计更合理。
如何自定义类加载器?
自定义类加载器只需要直接继承java.lang.ClassLoader,该类有两个核心方法
- loadClass(String name, boolean resolve),这个方法实现了双亲委派机制。
- findClass(String name),默认是空实现,自定义类加载器重写findClass即可。
如何打破双亲委派机制?
在自定义类加载中提高过,java.lang.ClassLoader中有两个核心方法,loadClass方法中实现了双亲委派机制,如果想要打破双亲委派机制,我们只需要自己重新loadClass方法,自定义类加载逻辑即可。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。