以下是看了诸葛老师的教程,做一下笔记,方便复习使用
1、类加载过程
首先类加载到使用的过程会涉及到以下几个步骤:
加载 ---> 验证 ---> 准备 ---> 解析---> 初始化 ---> 使用 ---> 卸载
加载:在硬盘上查找并通过IO读入字节码文件,使用到类时才会加载,例如调用类的main()方法,new对象等等
验证:校验字节码文件的正确性
准备:给类的静态变量分配内存,并赋予默认值
解析:将符号引用替换为直接引用,该阶段会把一些静态方法(符号引用,比如main()方法)替换为指向数据所存内存的指针或句柄等(直接引用),这是所谓的 静态链接过程(类加载期间完成),动态链接是在程序运行期间完成的将符号引用替换为直接引用,下节课会讲到动态链接
初始化:对类的静态变量初始化为指定的值,执行静态代码块
2、类加载器
上面的类加载过程主要是通过类加载器来实现的,Java里有如下几种类加载器
启动类加载器:负责加载支撑JVM运行的位于JRE的lib目录下的核心类库,比如 rt.jar、charsets.jar等
扩展类加载器:负责加载支撑JVM运行的位于JRE的lib目录下的ext扩展目录中 的JAR类包
应用程序类加载器:负责加载ClassPath路径下的类包,主要就是加载你自己写 的那些类
自定义加载器:负责加载用户自定义路径下的类包
看一个类加载器示例:
public class Demo { public static void main(String[] args){ System.out.println(String.class.getClassLoader()); System.out.println(com.sun.crypto.provider.DESKeyFactory.class.getClassLoader().getClass().getName()); System.out.println(Demo.class.getClassLoader().getClass().getName()); System.out.println(ClassLoader.getSystemClassLoader().getClass().getName()); } } 运行结果: null sun.misc.Launcher$ExtClassLoader sun.misc.Launcher$AppClassLoader sun.misc.Launcher$AppClassLoader
3、双亲委派机制
1. 首先,检查一下指定名称的类是否已经加载过,如果加载过了,就不需要再加载,直接返回。
2. 如果此类没有加载过,那么,再判断一下是否有父加载器;如果有父加载器,则由父加载器加载(即调用parent.loadClass(name, false);).或者是调用bootstrap类加载器来加载。
3. 如果父加载器及bootstrap类加载器都没有找到指定的类,那么调用当前类加载器的findClass方法来完成类加载。
说的简单点的话就是 :先找父亲加载,不行再由儿子自己加载
接下来看一下源码及自己添加的注释:
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException { synchronized (getClassLoadingLock(name)) { // First, check if the class has already been loaded //name是类名 , 判断当前的类是否被加载 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(); //最终最终还是为空的话,就去自己去执行,即findClass 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; } }
4、为什么要设计双亲委派机制?
沙箱安全机制:自己写的java.lang.String.class类不会被加载,这样便可以防止核心API库被随意篡改
避免类的重复加载:当父亲已经加载了该类时,就没有必要子ClassLoader再加载一次,保证被加载类的唯一性