类加载概述
2017-03-24 本文已影响55人
Hughman
(1)类的加载方式:
方式一:命令行启动应用时,由JVM初始化加载
方式二:通过Class.forName()方法动态加载(默认会执行初始化块,但如果指定ClassLoader,初始化时不执行静态块)
方式三:通过ClassLoader.loadClass()方法动态加载(不会执行初始化块)
(2)类加载机制
虚拟机把描述类的数据从Class文件加载到内存,并对数据进行校验、转换解析和初始化,最终形成能被虚拟机直接使用的Java类型。
(3)类加载时机
Paste_Image.png注意:
1.顺序问题:
加载、验证、准备、初始化和卸载的顺序是确定的,解析阶段不一定,在某些情况下可以初始化后开始,从而支持Java语言的运行时绑定(动态绑定或者晚期绑定)
2.初始化阶段有且仅有5种情况(主动引用):
- 遇到new、getstatic、putstatic或invokestatic这4条字节码指令时,若类没有进行初始化,先触发其初始化(对应的场景:使用new关键字实例化对象,读取或设置一个类的静态字段,以及调用一个类的静态方法);
- 使用java.lang.reflect包的方法对类的进行反射调用时,若没有对类初始化;
- 初始化一个类时,发现父类还未初始化,先触发父类的初始化;
- 当虚拟机启动时,用户需指定一个要执行的主类,虚拟机先初始化这个主类;
- 当使用jdk1.7动态语言支持时,若一个java.lang.invoke.MethodHandle实例最后的解析结果是REF_getStatic、REF_putStatic、REF_invokeStatic的方法句柄,且这个方法句柄对应的类没有进行初始化过,则先触发初始化。
(总结:5种情况都是对一个类进行主动引用:遇到四种指令、反射调用、父类未初始化、用户指定的主类初始化、jdk1.7中动态调用解析结果的方法句柄)
反例:被动引用不会触发初始化:
- 通过子类引用父类的静态字段,不会导致子类初始化;(只会触发父类的初始化)
- 通过数组定义来引用类,不会触发此类的初始化;
- 常量在编译阶段会存入调用类的常量池中,本质上并没有直接引用到定义常量的类,不会触发定义常量的类的初始化;
3.接口的加载过程:
与类加载过程稍有不同,接口中不能使用static{}语句块,但编译器仍然会为接口生成“<clinit>()”类构造器,用于初始化接口中成员变量。
真正的区别是:初始化一个类时,接口不要求其父接口全部完成初始化,只有真正使用父接口的时候才会初始化。
类从被加载到虚拟机内存中开始,到卸载出内存为止,类的生命周期:加载——验证——准备——解析——初始化——使用——卸载。7个阶段。
连接:是验证——准备——解析三个部分
(4)类加载的过程
1.加载2.验证3.准备4.解析5.初始化
(5)三大类加载器:
启动类加载器——扩展类加载器——应用程序类加载器