加载:将类信息加载至方法区中,在堆中实例一个Class对象。
根据类的全路径加载Class文件,从jar读取class文件,根据一定的规则实时生成,如动态代理, 根据相关的类自动生成代理类。
不是jvm启动时就加载,而是在使用时触发加载。主要加载场景如下:
a. new 一个类
b. 调用类的静态方法,以及读取或者修改一个类的静态字段的时候(非常量)
c. 该类为程序的入口
d. 类反射时有上述行为。
链接:加载和链接一般交叉进行。
a. 验证:字节码是否合法,方法和字段名称是否重复等
b. 准备:静态变量分配内存空间以及静态变量赋默认初始值。
c. 解析:将符号引用转化为直接引用(如将printf这个方法名 转换为直接的内存地址)。jvm在该阶段将类名、方法名、属性名等转化为直接的内存地址。
初始化:执行静态变量的初始化以及静态的Java代码块,并赋值设置的变量值。
由于初始化必须是加载完成后,所以二者的时机类似:
a. new 一个类
b. 调用类的静态方法,以及读取或者修改一个类的静态字段的时候(非常量)
c. 该类为程序的入口
d. 类反射时有上述行为。
e. 初始化一个类的子类,该子类的所有的父类都会被初始化
过程:顺序执行变量赋值语句与静态代码块,若有父类,会按照上述顺序优先执行父类相关数据的初始化。
使用:分为主动引用和被动引用
主动引用:会引起类的初始化
a.new 一个类的时候
b.调用类的静态方法,以及读取或者修改一个类的静态字段的时候(不是常量)
c.这个类是程序的入口类
d.对这个类进行反射的时候(执行了上面的行为)
注意:以上几种主动引用会引起类的初始化,这里的几种情况只有new或者使用class.newInstance()才会调用构造函数!
被动引用:不会引起类的初始化
a.引用父类的静态字段
b.定义类数组
c.引用类常量
Class.forName()和ClassLoader.loadClass的区别
Class.forName(className)默认调用的方法是Class.forName(className,true,classloader);第二个参数表示是否进行初始化,默认为进行初始化。一旦初始化,会进行静态代码块的初始化以及静态变量的初始化。
ClassLoader.loadClass(className)方法,内部实际调用的方法是 ClassLoader.loadClass(className,false);第2个 boolean参数,表示目标对象是否进行链接,false表示不进行链接,不进行链接意味着不进行包括初始化等一些列步骤,那么静态块和静态对象就不会得到执行。