1、代码从主方法加载到jvm的过程
2、几种类加载器
引导类加载器(bootStrap):比如这Math.class文件,通过引导类加载器可以帮我们从jre目录下加载一些核心包。
扩展类加载器(ExtClassLoader):加载jre中ext目录相关的类
应用类加载器(AppClassLoader):加载我们的类Math.class,我们的类默认使用AppClassLoader
自定义类加载器:可以继承ClassLoader类,重写findClass方法、loadClass等方法实现自己的类加载器
3、双亲委派机制
几个类加载器之间的关系
这个是类图,不是双亲委派依赖关系
3.1 双亲委派机制的定义
当我们从主函数进入,加载某个类时,优先选择AppClassLoader加载器进行加载当前类,判断当前类是否已经加载过这个类,若未加载过,则委托其parent属性所对应的加载器进行加载,若最后还是未找到,则会通过使用当前类加载器findClass(name),去找URLClassLoader中的findClass方法,再去本地找。
3.2 双亲委派中的parent关系
自定义类加载器的parent是AppClassLoader
AppClassLoader的parent是ExtClassLoader
ExtClassLoader没有parent属性,这时会走else条件,使用BootStrap方法加载
3.3双亲委派中的parent关系源码
在Launcher的构造器中,分别给AppClassLoader与ExtClassLoader的parent赋了值。
AppClassLoader的parent为var2,也就是ExtClassLoader
AppClassLoader(URL[] var1, ClassLoader var2) {
super(var1, var2, Launcher.factory);
this.ucp.initLookupCache(this);
}
ExtClassLoader的parent为null
public ExtClassLoader(File[] var1) throws IOException {
super(getExtURLs(var1), (ClassLoader)null, Launcher.factory);
SharedSecrets.getJavaNetAccess().getURLClassPath(this).initLookupCache(this);
}
3.4 注意
双亲委派机制中的parent并不是真的指其父类,而是parent属性所对应的类
3.5 双亲委派机制核心源码
位置:java.lang.ClassLoader#loadClass(java.lang.String, boolean)
protected Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException
{
synchronized (getClassLoadingLock(name)) {
// 首先,看这个类(name参数所代表的类)是否已经被加载过
Class<?> c = findLoadedClass(name);
//若未被加载过,就找其parent属性所对应的加载器
if (c == null) {
long t0 = System.nanoTime();
try {
//若这个加载器有parent
if (parent != null) {
//使用其parent进行加载
c = parent.loadClass(name, false);
} else {
//该类没有parent,则直接使用bootstrap寻找,也就是我
//我们所说的,在jre的lib目录下的包中寻找是否有
c = findBootstrapClassOrNull(name);
}
} catch (ClassNotFoundException e) {
// ClassNotFoundException thrown if class not found
// from the non-null parent class loader
}
//若仍然加载失败
if (c == null) {
// If still not found, then invoke findClass in order
// to find the class.
long t1 = System.nanoTime();
//就去urlClassLoader中寻找(当前类加载器本地寻找)
c = findClass(name);
// 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);
}
return c;
}
}
3.6 为什么要设计双亲委派机制?
沙箱安全机制:自己写的java.lang.String.class类不会被加载,这样便可以防止核心API库被随意篡改
避免类的重复加载:当父亲已经加载了该类时,就没有必要子ClassLoader再加载一次,保证被加载类的唯一性