正文
protected ClassLoader() { this(checkCreateClassLoader(), getSystemClassLoader());
}
执行以下代码即可获得classpath加载路径:
System.out.println(System.getProperty("java.class.path"));
2.4 三种加载器联系
用一张图来表示三张图的关系如下:
用户自定义的无参加载器的父类加载器默认是AppClassloader加载器,而AppClassloader加载器的父加载器是ExtClassloader,通过下面代码可以验证:
ClassLoader.getSystemClassLoader().getParent()
一般我们都认为ExtClassloader的父类加载器是BootStarpClassloader,但是其实他们之间根本是没有父子关系的,只是在ExtClassloader找不到要加载类时候会去委托BootStrap加载器去加载。
通过如下代码可以知道父加载器为null
ClassLoader.getSystemClassLoader().getParent().getParent()
2.5 类加载器原理
Java类加载器使用的是委托机制,也就是子类加载器在加载一个类时候会让父类来加载,那么问题来了,为啥使用这种方式那?因为这样可以避免重复加载,当父亲已经加载了该类的时候,就没有必要子ClassLoader再加载一次。考虑到安全因素,我们试想一下,如果不使用这种委托模式,那我们就可以随时使用自定义的String来动态替代java核心api中定义的类型,这样会存在非常大的安全隐患,而双亲委托的方式,就可以避免这种情况,因为String已经在启动时就被引导类加载器(Bootstrcp ClassLoader)加载,所以用户自定义的ClassLoader永远也无法加载一个自己写的String,除非你改变JDK中ClassLoader搜索类的默认算法。下面我们从源码看如何实现委托机制:
protected Class> loadClass(Stringname,boolean resolve)
throws ClassNotFoundException
{
synchronized (getClassLoadingLock(name)) {
// 首先从jvm缓存查找该类
Class c = findLoadedClass(name); (1) if (c ==null) {
longt0 = System.nanoTime();
try { //然后委托给父类加载器进行加载
if (parent !=null) {
c = parent.loadClass(name,false); (2)
} else { //如果父类加载器为null,则委托给BootStrap加载器加载
c = findBootstrapClassOrNull(name); (3)
}
} catch (ClassNotFoundExceptione) {
// ClassNotFoundException thrown if class not found
// from the non-null parent class loader
}
if (c ==null) {
// 若仍然没有找到则调用findclass查找
// to find the class.
longt1 = System.nanoTime();
c = findClass(name); (4) // this is the defining class loader; record the stats
sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 -t0);
sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
sun.misc.PerfCounter.getFindClasses().increment();
}
}
if (resolve) {
resolveClass(c);
}
returnc;
}
}
分析代码知道首先会执行(1)从jvm缓存查找该类,如何该类之前被加载过,则直接从jvm缓存返回该类,否者看当前类加载器是否有父加载器,如果有的话则委托为父类加载器进行加载(2),否者调用(3)委托为BootStrapClassloader进行加载,如果还是没有找到,则调用当前Classloader的findclass方法进行查找。
从上面源码知道要想修改类加载委托机制,实现自己的载入策略 可以通过覆盖ClassLoader的findClass方法或者覆盖loadClass方法来实现。
2.6 Java中如何构造三种类加载器的结构
下面从源码来分析下JVM是如何构建内置classloader的,具体是rt.jar包里面sun.misc.Launcher类:
public Launcher()
{
ExtClassLoader localExtClassLoader;