类加载主要有三个过程:loading 、linking 、initializing
其中linking又分为三个步骤:verification 、preparation 、resolution
1:loading是把一个class文件加载到内存中
2:接下来就是Linking,分为三小步
2.1:verification 用来校验加载进来的class文件是否符合class文件标准,如果不符合直接拒绝
2.2:preparation 将class文件的静态变量赋默认值而不是初始值,如 static int i = 8;这个步骤并不是将 i 赋值为8,而是赋值默认值0
2.3:resolution 把class文件常量池中用到的符号引用转换成内存地址,可以访问到的内容
3:initialzing 成为初始化,静态变量这个时候才会被赋值为初始值
下面为loading的过程图
类加载器的加载过程是分成不同的层次来加载的,不同的类加载器加载不同的class文件,Bootstrap > Extension > App > Custom(自定义类加载器)
1:第一个类加载器:Bootstrap 称为启动类加载器,是Java类加载层次中最顶层的类加载器,负责加载JDK中的核心类库
2:第二个类加载器:Extension 是用来加载扩展类的,主要加载Java的扩展类库,默认加载JAVA_HOME/jre/lib/ext/目录下的所有jar包
3:第三个类加载器:Application 称为系统类加载器,负责在JVM启动时,加载来自Java中的classpath或者java.class.path系统属性或者
CLASSPATH操作系统属性所指定的jar类包和类路径
4:第四个类加载器:CustomClassLoader(自定义加载器)
文字描述:
用比较通俗的话来解释loading这个过程,当有一个类需要被加载时,首先要判断这个类是否已经被加载到内存,判断加载与否的过程是有顺序的,
如果有自己定义的类加载器,会先到custom class loader 的cache(缓存)中去找是否已经加载,若已加载直接返回结果,
否则到App的cache中查找,如果已经存在直接返回,如果不存在,到Extension中查找,存在直接返回,
不存在继续向父加载器中寻找直到Bootstrap顶层,如果依然没找到,那就是没有加载器加载过这个类,需要委派对应的加载器来加载,
先看看这个类是否在自己的加载范围内,如果是直接加载返回结果,若不是继续向下委派,以此类推直到最下级,
如果最终也没能加载,就会直接抛异常 ClassNotFoundException,这就是双亲委派模式。
public static void main(String[] args) {
System.out.println("----------------Bootstrap加载类----------------"); String property = System.getProperty("sun.boot.class.path"); String s = property.replaceAll(";", System.lineSeparator()); System.out.println(s); System.out.println("-------------------Ext加载类-------------------"); String property1 = System.getProperty("java.ext.dirs"); String s1 = property1.replaceAll(";", System.lineSeparator()); System.out.println(s1); System.out.println("-------------------App加载类-------------------"); String property2 = System.getProperty("java.class.path"); String s2 = property2.replaceAll(";", System.lineSeparator()); System.out.println(s2); }
----------------Bootstrap加载类----------------
C:Program FilesJavajdk1.8.0_201jrelib
esources.jar
C:Program FilesJavajdk1.8.0_201jrelib
t.jar
C:Program FilesJavajdk1.8.0_201jrelibsunrsasign.jar
C:Program FilesJavajdk1.8.0_201jrelibjsse.jar
C:Program FilesJavajdk1.8.0_201jrelibjce.jar
C:Program FilesJavajdk1.8.0_201jrelibcharsets.jar
C:Program FilesJavajdk1.8.0_201jrelibjfr.jar
C:Program FilesJavajdk1.8.0_201jreclasses
-------------------Ext加载类-------------------
C:Program FilesJavajdk1.8.0_201jrelibext
C:WindowsSunJavalibext
-------------------App加载类-------------------
C:Program FilesJavajdk1.8.0_201jrelibcharsets.jar
C:Program FilesJavajdk1.8.0_201jrelibdeploy.jar
C:Program FilesJavajdk1.8.0_201jrelibextaccess-bridge-64.jar
C:Program FilesJavajdk1.8.0_201jrelibextcldrdata.jar
C:Program FilesJavajdk1.8.0_201jrelibextdnsns.jar
C:Program FilesJavajdk1.8.0_201jrelibextjaccess.jar
C:Program FilesJavajdk1.8.0_201jrelibextjfxrt.jar
C:Program FilesJavajdk1.8.0_201jrelibextlocaledata.jar
C:Program FilesJavajdk1.8.0_201jrelibext
ashorn.jar
C:Program FilesJavajdk1.8.0_201jrelibextsunec.jar
C:Program FilesJavajdk1.8.0_201jrelibextsunjce_provider.jar
C:Program FilesJavajdk1.8.0_201jrelibextsunmscapi.jar
C:Program FilesJavajdk1.8.0_201jrelibextsunpkcs11.jar
C:Program FilesJavajdk1.8.0_201jrelibextzipfs.jar
C:Program FilesJavajdk1.8.0_201jrelibjavaws.jar
C:Program FilesJavajdk1.8.0_201jrelibjce.jar
C:Program FilesJavajdk1.8.0_201jrelibjfr.jar
C:Program FilesJavajdk1.8.0_201jrelibjfxswt.jar
C:Program FilesJavajdk1.8.0_201jrelibjsse.jar
C:Program FilesJavajdk1.8.0_201jrelibmanagement-agent.jar
C:Program FilesJavajdk1.8.0_201jrelibplugin.jar
C:Program FilesJavajdk1.8.0_201jrelib
esources.jar
C:Program FilesJavajdk1.8.0_201jrelib
t.jar
E:ProjectJavaoutproductionJava
D:IntelliJ IDEAIntelliJ IDEA 2019.2.4libidea_rt.jar
为什么要搞双亲委派?
主要是为了安全,这里可以使用反证法,如果任何加载器都可以把Class加载到内存中,我们就可以自定义加载器来加载java.lang.string,在打包时可以把密码存为String对象,这样会造成安全问题
如何打破双亲委派?
重写loadclass方法
自定义加载器
我们自定义的类加载器只需要继承ClassLoader,并覆盖findClass方法