大家好,又见面了,我是你们的朋友全栈君。
前言
我们Javaer都知道类想要被加载是需要一个个ClassLoader来执行的,并且类加载的方案叫双亲委派模式,说是双亲,其实就是单亲,可能我们最初的翻译人想让我们的加载器的家庭更完整吧,所以翻译成双亲。默认的类加载器包括BootstrapClassLoader、ExtClassLoader、AppClassLoader,他们都定义在在rt.jar中的sun.misc.Launcher
类中,他们的”继承”关系是AppClassLoader—>ExtClassLoader—>BootstrapClassLoader,ExtClassLoader的parent获取不到BootstrapClassLoader,只能获取到一个null。
- bootstrap是C++编写的类加载器,主要加载%JRE_HOME/lib/目录下的jar包
- ExtClassLoader主要加载%JRE_HOME/lib/ext目录下的jar包
- AppClassLoader主要加载java环境变量CLASSPATH所指定的路径下的jar包和class文件,通过System.getProperty(“java.class.path”)获取考验获取CLASSPATH路径
- 用户自定义ClassLoader,加载用户自己制定的类
getClassLoader会不会为空
说了这么多,其实就是想说getClassLoader当然可能会为空,是不是此时会有个疑惑:加载器都为空了,那这个类是怎么加载的,被谁加载的?答:被BootstrapClassLoader加载的。
我们知道BootstrapClassLoader是由C++编写的,我们是用Java代码获取不到的,BootstrapClassLoader也不是ExtClassLoader的父类,而是它的父亲,这里要搞清关系,父亲和父类是两码事,父类是有继承关系,父亲是上一层的关系,所以我们在获取String、Integer、int、double、BufferedInputStream等等一系列在rt.jar包中被BootstrapClassLoader加载的类的加载器时,返回的都是null。
ExtClassLoader是怎么成为AppClassLoader的父亲的
类加载器并非是继承关系,而是父子关系,就像上面说的BootstrapClassLoader是ExtClassLoader的父亲,不是父类。关键点就在于ClassLoader的实例变量parent,这个parent指定了当前类加载器的父亲,但是翻遍了AppClassLoader的代码也没发现是在哪里把ExtClassLoader设置进去的,怎么parent就是ExtClassLoader了呢。
刚才我们说这些类加载器被定义在了Launcher类中,那么我们就去看下这个类的构造器
public Launcher() {
Launcher.ExtClassLoader var1;
try {
// 获取ExtClassLoader
var1 = Launcher.ExtClassLoader.getExtClassLoader();
} catch (IOException var10) {
throw new InternalError("Could not create extension class loader", var10);
}
try {
// 获取AppClassLoader实例并赋值给loader,并把ExtClassLoader的实例传入到方法中,
this.loader = Launcher.AppClassLoader.getAppClassLoader(var1);
} catch (IOException var9) {
throw new InternalError("Could not create application class loader", var9);
}
// ...
}
// 返回AppClassLoader实例
public ClassLoader getClassLoader() {
return this.loader;
}
从Launcher的构造器我们看到关键点在AppClassLoader.getAppClassLoader(var1)这句,那我们就看这个方法是怎么写的
static class AppClassLoader extends URLClassLoader {
public static ClassLoader getAppClassLoader(final ClassLoader var0) throws IOException {
final String var1 = System.getProperty("java.class.path");
final File[] var2 = var1 == null ? new File[0] : Launcher.getClassPath(var1);
return (ClassLoader)AccessController.doPrivileged(new PrivilegedAction<Launcher.AppClassLoader>() {
public Launcher.AppClassLoader run() {
URL[] var1x = var1 == null ? new URL[0] : Launcher.pathToURLs(var2);
// 关键看这一句,调用了AppClassLoader的构造器,并把ExtClassLoader实例传了进去,那就跳到构造器去看
return new Launcher.AppClassLoader(var1x, var0);
}
});
}
// 构造器
AppClassLoader(URL[] var1, ClassLoader var2) {
// 调用了父类的构造器,他的父类是哪个?从定义上看应该是URLClassLoader没跑了
super(var1, var2, Launcher.factory);
this.ucp.initLookupCache(this);
}
}
其实再往里的代码就不用在这闲扯了,里面就是不断的将var2往上传递,直到ClassLoader这个类的构造器中,在ClassLoader中完成的设置。
那为什么ExtClassLoader没通过这种形式将BootstrapClassLoader设置给parent呢?
是不是傻,上面刚说了BootstrapClassLoader是C++写的,Java代码不能直观的获取。
发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/141668.html原文链接:https://javaforall.cn
【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛
【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...