java类加载的时机和触发类的初始化的条件

类从被加载到虚拟机内存中开始,到卸载出内存为止,它的整个生命周期包括:加载,验证,准备,解析,初始化,使用,卸载7个阶段。其中验证、准备、解析三个部分统称为连接。7个阶段的顺序如图:

这里写图片描述

加载、验证、准备、初始化和卸载这5个阶段的顺序是肯定的,类的加载过程必须按照这种顺序按部就班地开始,而解析阶段则不一定:它在某些情况下可以在初始化阶段后再开始,这是为了支持Java语言的运行时绑定(也被称为动态绑定或者晚期绑定)。注意,这里笔者写的是按部就班地“开始”,而不是按部就班地“进行”或“完成”,强调这点是因为这些阶段通常都是互相交叉地混合式进行的,通常会在一个阶段执行的过程中调用、激活另外一个阶段。

什么情况下需要开始类加载过程的第一个阶段:加载?Java虚拟机规范中并没有进行强制约束,这点可以交给虚拟机的具体实现来自由把握。但是对于初始化阶段,虚拟机规范则是严格规定了下面的几种情况必须立即对类进行“初始化”(而加载、验证、准备自然需要在此之前开始):

  • 创建类的实例
  • 访问类的静态变量(注意:当访问类的静态并且final修饰的变量时,不会触发类的初始化。),或者为静态变量赋值。
  • 调用类的静态方法(注意:调用静态且final的成员方法时,会触发类的初始化!一定要和静态且final修饰的变量区分开!!
  • 使用java.lang.reflect包的方法对类进行反射调用的时候,如果类没有进行过初始化,则需要先触发其初始化。如:Class.forName("bacejava.Langx");
  • 注意通过类名.class得到Class文件对象并不会触发类的加载。
  • 初始化某个类的子类
  • 直接使用java.exe命令来运行某个主类(java.exe运行,本质上就是调用main方法,所以必须要有main方法才行)。
  • 注意:通过子类调用父类的静态成员时,只会初始化父类而不会初始化子类。 因为没有调用子类的相关静态成员,这也叫做能不加载不加载原则。
  • 注意:调用静态成员时,会加载静态成员真正所在的类及其父类。 这也好理解,因为本类的父类没有加载就会先去加载父类。
  • 注意:类的加载成功后,即静态成员都被加载后,是不会再加载第二次的。只有非静态成员,如非静态成员变量、非静态代码块、非静态方法(不调用不加载)、构造方法都会被多次实例化的时候多次加载。
  • 注意:如果静态属性有 final 修饰时,则不会加载,当成常量使用。例:public static final int a =123; public static final int a=5/3*2+1;public static final double a=Math.PI;等
  • 注意:但是静态属性 有final修饰 时也有情况会被加载,public static final int a=getNum();这样也会被加载。getNum()是静态方法,并且不管这个静态方法是子类的还是父类的;
  • 在上面触发类被初始化的情况称为对类的主动引用,除此之外,那些引用类的方式没有触发初始化的叫做被动引用

在《深入理解java虚拟机之jvm高级特性与最佳实践》中有一段耐人寻味的讲解:

阅读更多
换一批

没有更多推荐了,返回首页