java:类的加载机制(二)
1.引言
写简书,第一次觉得好想去分享知识。也体会到知识带给我的充实感,相信自己这样下去,2年之后一定能达到我的目标。java类加载机制主要还是看的这篇博客 深入理解java类加载器。这个原文作者真的是太厉害了。写的很详细,我也很有收获。
2.正题
前面说了一个类的生命周期:
Paste_Image.png加载的过程是将二进制的class文件。放进堆中,在堆中形成一个Class对象。这个过程需要用到3种类加载器。
启动(Bootstrap)类加载器:
主要加载jdk中jre/lib/rt.jar中的类。具体加载的那些文件,那些jar包可以自己解压看看。
扩展(Extension)类加载器:
扩展类加载器是由Sun的ExtClassLoader(sun.misc.Launcher$ExtClassLoader)实现的。加载jre/lib/ext/*.jar
系统(System)类加载器:
系统类加载器是由 Sun的 :AppClassLoader(sun.misc.Launcher$AppClassLoader)实现的。加载
classpath指定的jar或者目录。(我理解就是项目中的jar,或者java文件)
这三个加载的关系是:是系统类加载器的父类加载器是标准扩展类加载器,标准扩展类加载器的父类加载器是启动类加载器
Paste_Image.png代码证明:
public class LoaderTest {
public static void main(String[] args) {
try {
System.out.println(ClassLoader.getSystemClassLoader());
System.out.println(ClassLoader.getSystemClassLoader().getParent());
System.out.println(ClassLoader.getSystemClassLoader().getParent().getParent());
} catch (Exception e) {
e.printStackTrace();
}
}
}
输出的结果是:
sun.misc.Launcher$AppClassLoader@6d06d69c
sun.misc.Launcher$ExtClassLoader@70dea4e
null
第三个输出的结果:之所以是为null。是因为Bootstrap是C启动生成的对象。(暂时这样理解,可能不准确,没深究这个东西)。
类加载的算法:
双亲委派: 通俗的讲,就是某个特定的类加载器在接到加载类的请求时,首先将加载任务委托给父类加载器,依次递归,如果父类加载器可以完成类加载任务,就成功返回;只有父类加载器无法完成此加载任务时,才自己去加载。
源码分析:
protected Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException
{
synchronized (getClassLoadingLock(name)) {
// First, check if the class has already been loaded
Class<?> c = findLoadedClass(name);
if (c == null) {
long t0 = System.nanoTime();
try {
if (parent != null) {
c = parent.loadClass(name, false);//父类开始加载
} else {
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();
c = findClass(name);//依旧为null,那么自己开始加载
// 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;
}
}
这段代码结合debug模式,很容易懂,上面标注了一下注释。
Class.forName
Class.forName 我第一次接触是在数据库的学习载入驱动,当时不懂为什么要这样搞,只知道需要,然而现在懂了。Class.forName 就是调用加载器加载类,从而在堆中形成一个Class对象。Class.forName(String name)默认会使用调用类的类加载器来进行类加载。
现在做一个测试:验证下上面提到的结论:
扩展(Extension)类加载器,加载jre/lib/ext/*.jar;AppClassLoader加载
classpath指定的jar或者目录。
将Test的class打包的gif图:
demo2.gif打包完毕将其放在jre/lib/ext/下面。新建一个项目,这个时候就会看到test.jar 出现在project 下面的External Libraries中。(提示:可能一次还加载不出来,要确确实实的关闭了jvm,这样下次才会出现)。
之后再运行:可以很清晰的看到出来的是Extension类加载器。
demo3.gif从结果可以看出上面的结论没有错。。明天再写下自定义加载器等知识点,做一下笔记。