c++实现的部分调用过程:java user.class命令,调用java.exe 创建java虚拟机,然后创建一个引导类加载器bootstrapLoader,由该类加载器去调用java内部的类launcher.getLauncher()方法初始化该类(sun.mis.launcher类是在rt.jar包下,由bootstrapLoader加载到jvm内存,加载路径为jdk1.8/jre/lib下的class),然后该类去实例化java内部的类加载器,主要是扩展类加载器、应用程序类加载器这两个类加载器,并调用launcher.getClassLoader()获取应用类加载器(AppClassLoader类加载器默认加载的路径是我们设置的classpath),然后由AppClassLoader调用loadClass(类路径)加载对应的类,执行main()方法。
其中类加载过程loadClass()有以下几个过程:
加载–》验证–》准备–》--》解析–》初始化–》使用–》卸载
加载:就是把字节码文件从磁盘经过io流读取到内存里面的,使用的时候才会加载类,例如调用类的main()方法,new对象等等,在加载阶段会在内存生成一个代表这个类的java.lang.class的对象,作为方法区这个类的各种数据的访问入口。
验证:校验字节码文件的正确性(文件是否损坏或者符合字节码规范)
准备:类的静态变量分配内存,赋初始值(int 为0,boolean为false,对象类型为null)
解析:将符号引用替换为直接引用,该阶段会把一些静态方法(符号引用,比如main()方法替换为指向数据所存的指针或句柄等等(直接引用)),这就是静态链接过程(类加载期间完成),动态链接是在程序运行期间完成的将符号引用替换为直接引用。
初始化:以上过程执行完后,对类的静态变量初始化赋值为指定的值也就是字面量,执行静态代码块。
而class类对象是存放在JVM的方法区中:运行时常量池,类型信息、字段信息、方法信息、类加载器的 引用、对应class实例的引用等信息
类加载器的引用:被加载的类所引用的类加载的实例。例如user.class.getClassLoader().getClass().getName()
对应class实例的引用:类加载器加载类信息(user.class)放到方法区后,会创建一个对应的Class类型的对象实例方到堆中,作为开发人员访问方法区中类定义的入口和切入点
启动类加载器:负责加载支撑JVM运行的位于JRE的lib目录下的核心类库,比如 rt.jar、charsets.jar等
扩展类加载器:负责加载支撑JVM运行的位于JRE的lib目录下的ext扩展目录中 的JAR类包
应用程序类加载器:负责加载ClassPath路径下的类包,主要就是加载你自己写 的那些类
简单的案例:
引导类加载器为null是因为启动类加载器是由c++实现。
自定义加载器:负责加载用户自定义路径下的类包
自定义类加载
自定义类加载只需继承 java.lang.ClassLoader 类,该类有两个核心的方法:
1.loadClass
以上代码逻辑就是实现了双亲委派机制
执行过程:
1.根据传入的name参数,即全类路径,检查一下该类是否已经加载过了,如果已经加载,则不再加载,直接返回该类。
2.如果此类没有加载过,那么再判断一下是否有父加载器,如果有父加载器,就由父加载器加载(即调用parent.loadClass(name, false)),如果没有父类加载器,则由引导类加载器加载(bootstrap类)
3.如果父加载器和引导类(bootstrap)都找不到指定的类,则调用当前类的加载器的findClass方法来完成类加载。
2.findclass
根据类路径去找该类。
自定义类加载只需继承 java.lang.ClassLoader 类,然后要重写findClass方法
初始化自定义类加载器,会先初始化父类ClassLoader,其中会把自定义类加载器的父加载器设置为应用程序类加载器AppClassLoader
类的加载机制不变,还是双亲委派机制
先是网上委托,如果父加载器能找到,则直接返回,如果父加载器找不到,则由当前自定义类加载的找,根据自定义类路径去找。
为什么要设计双亲委派机制?
沙箱安全机制:自己写的java.lang.String.class类不会被加载,这样便可以防止核心API库被随意篡改
避免类的重复加载:当父亲已经加载了该类时,就没有必要子ClassLoader再加载一次,保证被加载类的唯一性
有时候项目工程已存在别人写的,但是又不能修改,如果要多个类共存,比如jar的不同版本的共存的问题怎么办,可能存在api变动,导致其他系统出问题?
需要打破双亲委派来解决这个问题
1.自定义一个类加载器,继承ClassLoader 类
2.重写loadClass()方法,findClass()方法
打破双亲委派的意思就是,自己写的类不委托给父加载器,但是自己写的类依赖java.lang.Object,怎么解决这个问题呢?
解决:在loadClass()加判断,如果不是自定义的类,则由父加载器加载,否则由自定义类加载类。