java面试总结 -------双亲委派模型
双亲委派模型是类加载器中一个重要的知识点。
类加载器模型
根据上图类加载模型,类加载器由三部分组成,
- 位于最上端也就是顶层父类的是
根类加载器
,它只加载%JAVA_HOME%/jre/lib/ext,此路径下的所有classes目录以及java.ext.dirs系统变量指定的路径中类库,此加载器由c++实现。 - 然后下一层即根类加载器的子类加载器是
拓展类加载器
,由根类加载器加载,拓展类加载器只加载%JAVA_HOME%/jre/lib/ext,此路径下的所有classes目录以及java.ext.dirs系统变量指定的路径中类库。 - 再下面就是
应用类加载器
,由拓展类加载器加载,应用类加载器为Java默认加载器. - 然后再下面就是用户的自定义加载器,由应用类加载器加载。
所以,从上到下为父子关系,且三个加载器中除了根类加载器,拓展类加载器
、应用类加载器
,有且只有一个父加载器。
双亲委托机制流程(见上图)
- 当一个类需要加载时,当前ClassLoader首先从自己已经加载的类中查询是否此类已经加载,如果已经加载则直接返回原来已经加载的类。且每个类加载器都有自己的加载缓存,当一个类被加载了以后就会放入缓存,等下次加载的时候就可以直接返回了。
- 如果当前类没有加载,则会向父加载器查询是否加载,若有,则返回类,若无,再向父加载器的父加载器查询,周而复始下去,知道查询到了根类加载器。
- 如果根类加载器有,则返回,若无,则从根类加载器向下尝试是否能加载,再从根类加载器尝试是否能加载,不能则交给子类尝试加载,周而复始直到能加载此类为止。
- 一般来说,用户的自定义类均从
应用类加载器
加载,即从下往上找是否加载到根类加载器,再从上往下交给应用类加载器加载完成并返回。
实例详解
public class MyTest5 {
public static void main(String[] args) throws Exception{
Class<?> clazz = Class.forName("java.lang.String");
System.out.println(clazz.getClassLoader());
}
}
在这个例子中,我们先用Class.forName
获得了String的类,然后使用getClassLoader
方法获得该类的加载器,在ClassLoader
的getClassLoader
方法源码中,有这么一行注释:
Returns the class loader for the class. Some implementations may use
null to represent the bootstrap class loader. This method will return
null in such implementations if this class was loaded by the bootstrap
class loader.
该注释大概意思时,该方法返回类的类加载器,如果该类的加载器是根类加载器就返回null。
由于String方法是属于jt.jar包的所以由根类加载加载,输出结果为null。
public class MyTest5 {
public static void main(String[] args) throws Exception{
Class<?> clazz = Class.forName("classloader.MyTest5");
System.out.println(clazz.getClassLoader());
}
}
这里我们输出用户自定义类的类加载器,由双亲委派机制可以得出,该类的类加载器为应用类加载器,所以它的输出结果为:
即应用类加载器(AppClassLoader)
另:getClassLoader方法不是对类的主动使用
public class MyTest6 {
public static void main(String[] args) {
ClassLoader classLoader=ClassLoader.getSystemClassLoader();
while (null!=classLoader){
System.out.println(classLoader);
classLoader = classLoader.getParent();
}
System.out.println(classLoader);
}
}
在这段代码中,我们先获得了系统类加载器(即应用类加载器),然后循环输出父类直到为null为止,
该代码输出结果为:
从这张图中,可以看出,系统加载器的父加载器是拓展类加载器,拓展类加载器的父加载器是根类加载器
(输出为null)
所以从实例三可以看出类加载器的层次关系。
几个重要api
clazz.getClasLoader()
:获得当前类的ClassLoaderThread.currentThread().getContextClassLoader()
:获得当前线程上下文的ClassLoaderClassLoader.getSystemCassLoader()
:获得系统的ClassLoaderDriverMangager,getCallerClassLoader()
:获得调用者的ClassLoader
资料:双亲委托机制