JVM把class文件加载到内存,并对数据进行校验、转换解析和初始化,最终形成JVM可以直接使用的Java类型的过程就是加载机制。
类从被加载到虚拟机内存中开始,到卸载出内存为止,它的生命周期包括了:加载(Loading)、验证(Verification)、准备(Preparation)、解析(Resolution)、初始化(Initialization)、使用(Using)、卸载(Unloading)七个阶段,其中验证、准备、解析三个部分统称链接。
加载(装载)、验证、准备、初始化和卸载这五个阶段顺序是固定的,类的加载过程必须按照这种顺序开始,而解析阶段不一定;它在某些情况下可以在初始化之后再开始,这是为了运行时动态绑定特性。值得注意的是:这些阶段通常都是互相交叉的混合式进行的,通常会在一个阶段执行的过程中调用或激活另外一个阶段。
加载:
什么情况下需要开始类加载过程中的第一个阶段(加载)呢?虚拟机规范中并没有强行的约束,而是由虚拟机的具体实现*把握。
在加载阶段,虚拟机需要完成以下三件事情:
通过一个类的全限定名来获取定义此类的二进制字节流。
将字节流所代表的静态存储结构转化为方法区的运行时数据结构。
在Java堆中生成一个代表这个类的java.lang.Class对象,作为方法区这些数据的访问入口。
二进制字节流可以来自本地的class文件或者Jar包、Zip包、War包等等,也可以来自网络
连接
连接为类的初始化做准备。
初始化:
对于初始化阶段,虚拟机规范严格规定了有且只有四种情况必须立即对类进行初始化(而加载、验证、准备自然需要在此之前开始):
遇到**new、getstatic、putstatic**或invokestatic这4条字节码指令时,如果类没有进行过初始化,则需要先触发其初始化。生成这4条字节码指令最常见的Java代码场景分别是:使用new关键字实例化对象、**读取一个类的静态字段**(被final修饰、已在编译阶段把结果放入常量池的静态字段除外)、设置一个类的静态字段、调用一个类的静态方法。
使用java.lang.reflect包的方法对类进行反射调用(例如Class.forName(className)、class.newInstance()、method.invoke(obj, args...)等等)的时候,如果类没有进行过初始化,则需要先触发其初始化。
当初始化一个类的时候,如果发现其父类还没有进行过初始化,则需要先触发其**父类**的初始化。
当虚拟机启动的时候,用户需要指定一个执行主类(含有main方法的类),虚拟机先初始化这个主类。
这四个场景中的行为称为对一个类进行主动引用。除此之外所有对类的引用方式,都不会触发初始化,称为被动引用。被动引用是否会触发类初始化之前(加载、验证)的动作,则视具体的情况和虚拟机的具体实现而定。
初始化会执行类的<client>()方法也就是静态方法
Class.forName和ClassLoader.loadClass的区别
Class.forName 提供两种方法,forName(String className)默认是对类进行初始化操作的。
而loadClass()只是将一个类加载到jvm虚拟机,如果要进行初始化操作,需要进行调用 object.newInstance()
public static Class<?> forName(String className)
throws ClassNotFoundException {
Class<?> caller = Reflection.getCallerClass();
return forName0(className, true, ClassLoader.getClassLoader(caller), caller);
}
Class.forName(String name, boolean initialize,ClassLoader loader)
Thread.currentThread().getContextClassLoader().loadClass(String name)