前言
我们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类中,那么我们就去看下这个类的构造器
1 | public Launcher() { |
从Launcher的构造器我们看到关键点在AppClassLoader.getAppClassLoader(var1)这句,那我们就看这个方法是怎么写的
1 | static class AppClassLoader extends URLClassLoader { |
其实再往里的代码就不用在这闲扯了,里面就是不断的将var2往上传递,直到ClassLoader这个类的构造器中,在ClassLoader中完成的设置。
那为什么ExtClassLoader没通过这种形式将BootstrapClassLoader设置给parent呢?
是不是傻,上面刚说了BootstrapClassLoader是C++写的,Java代码不能直观的获取。