类的加载器
作用当然是实现类的加载动作,但是不至于此。
对于确定一个类的唯一性是由他的类加载器和这个类本身决定。每一个类加载器都有一个独立的类名称空间。
双亲委派机智
站在虚拟机角度 只有2种加载器:
第一种是启动类加载器由C语言实现
第二种是其他所有类加载器 由Java实现。
而后来细分为3个系统提供的类加载器加载
1)启动类加载器
2)拓展类加载器
3)应用程序类加载器
双亲委派机制工作原理:
当一个类加载器收到加载类的请求,首先不会自己加载而是逐层将这个请求向上(父类加载器)转发,每一层都如此,因此最终回到启动类加载器进行加载,如果上层无法加载该类,则子类加载器才会自己尝试加载该类。
protected synchronized Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException
{
// 首先,检查请求的类是否已经被加载过了
Class c = findLoadedClass(name);
if (c == null) {
try {
if (parent != null) {
c = parent.loadClass(name, false);
} else {
c = findBootstrapClassOrNull(name);
}
} catch (ClassNotFoundException e) {
// 如果父类加载器抛出ClassNotFoundException
// 说明父类加载器无法完成加载请求
}
if (c == null) {
// 在父类加载器无法加载时
// 再调用本身的findClass方法来进行类加载
c = findClass(name);
}
}
if (resolve) {
resolveClass(c);
}
上述是双亲委派加载的实现,由此可见,加载器之间是以组合的方式而非继承。
破坏双亲委派机制
1)远古时代 java为了兼容已有代码,无法再以技术的手段避免loadClass()方法被子类覆盖。 后来重写findclass()方法解决了loadClass()问题
2)是由于双亲委派机制的模型自身缺陷导致的,基础类加载器一般总是被用户代码继承,所以为基础,但是并不是一层不变的。如JNDI服务,由启动类加载,但是JNDI存在的目的则是查找和集中管理,使得启动类加载器无法认识,加载那些代码。后来以线程上下文类加载器(Thread Context ClassLoader)。这个类加载器可以通过java.lang.Thread类的setContext-ClassLoader()方法进行设置,如果创建线程时还未设置,它将会从父线程中继承一个,如果在应用程序的全局范围内都没有设置过的话,那这个类加载器默认就是应用程序类加载器。后来在JDK6后以java.util.ServiceLoader类,以META-INF/services中的配置信息,以责任链模式,这才算是给SPI加载提供了合理的方案
3)热部署,动态加载等