类的初始化和生命周期详解以及脑图

857 阅读5分钟

类的初始化

主动引用【会初引起始化】

  • 1、使用New关键字实例化对象。
  • 2、访问某个类或接口的静态变量,或者对该静态变量赋值。
  • 3、调用类的静态方法.
  • 4、使用java.lang.reflect包下的方法对类进行反射调用的时候
  • 5、初始化一个类的子类时,发现其父类未初始化,优先初始化父类
  • 6、虚拟机启动的时候,用户需要指定一个主类,比如Main方法类,虚拟机会优先初始化这个主类。
  • 7、JDK1.7开始提供的动态语言支持,当java.lang.invoke.MethodHandler实例后的结果是REF-getStatic/REF_putstatic/REF_invokeStatic的句柄,并且这些句柄对应的类没初始化的话应该首先初始。

总结:只有当程序访问的静态变量或静态方法确实在当前类或当前接口中定义时,才可以认为是对类或接口的主动使用

被动引用【不会引起初始化】

  • 1、通过子类来引用父类的静态字段,只会触发父类的初始化,不会触发子类的初始化。
  • 2、通过数组定义来引用类,不会触发此类的初始化。SuperClass[] superClasses = new SuperClass[10]
  • 3、引用一个类的静态常量也不会触发初始化,因为常量在编译阶段已经确认。public static final int value = 22;

接口初始化

  • 1、当Java虚拟机初始化一个类时,要求它的所有父类都已经被初始化,但是这条规则并不适用于接口。
  • 2、在初始化一个类时,并不会先初始化它实现的接口。
  • 3、在初始化一个接口时,并不会先初始化它的父类。
  • 4、一个父类接口并不会因为它的子类接口或者实现类的初始化而初始化,只有当程序首次使用特定接口的静态变量时,才会导致该接口的初始化。

类的生命周期

加载

通过一个类全限定名来获取此类的二进制字节流。将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构。在内存中生成一个类代表这个类的java.lang.Class对象,作为方法区这个类的各种数据访问入口。


静态变量是怎么加载的?

  • 1、getStatic 获取类的静态变量.
  • 2、putStatic 给类的静态变量赋值.
  • 3、invokeStatic 调用类的静态方法.

查找并加载类的二进制数据,就是把二进制形式的Java类型读入Java虚拟机。

连接

验证

  • 文件格式验证:字节流是否符合class文件格式的规范。
  • 元数据验证:是否符合Java语言语法的规范。
  • 字节码验证:方法体进行校验分析,保证正确的运行。
  • 符号引用验证:常量池的各种符号引用信息进行匹配性校验。

准备

在准备阶段,Java虚拟机为类的静态变量分配内存,并将其初始为默认值。为类变量分配内存,设置默认值,但是在到达初始化之前,为类变量都没有初始化为正真的初始值。例如对于以下Fruit类,在准备阶段,将为int类型的静态变量a分配4个字节的内存空间,并且赋予默认值0,为long类型的静态变量b分配8个字节的内存空间,并且赋值为默认值0.

【代码片段】
class Fruit{
    public static int a = 1;
    public static long b;
    public static long c;
    static {
        b = 2L;
    }
}

解析

把类中的符号引用转化为直接引用,解析过程就是在类型的常量池中寻找类、接口、字段和方法的符号引用,把这些符号引用替换为直接引用的过程。

初始化

对类的静态变量,静态代码块执行初始化操作,为类变量赋于正确的初始值。

在初始化阶段,Java虚拟机执行类的初始化语句,为类的静态变量赋于初始值。在程序中,静态变量的初始化有两种途径:(1)在静态变量的声明处进行初始化(2)在静态代码块中进行初始化。例如【上面的代码片段】中,静态变量a和b都被显示初始化,而静态变量c没有被显示初始化,它将保持默认值0.

静态变量的声明语句,以及静态代码块都被看作类的初始化语句,Java虚拟机会按照初始化语句在类文件中的先后顺序来依此执行它们,例如 public static int a = 6;初始化以后这个静态变量a的取值为4.


类的初始化步骤

  1. 假如这个类还没有被加载和连接,那就先进行加载和连接。
  2. 假如类存在直接父类,并且这个父类还没有被初始化,那就先初始化直接父类。
  3. 假如类中存在初始化语句,那就依次执行这些初始化语句。

使用

卸载

清除该类的实例.

清除该类的ClassLoader引用.

清除该class对象的引用

举个栗子🌰


  • 问:public static int i = 100 怎么赋值的?
  • 答:1、准备阶段赋分配内存并将初始值为0. 2、初始化阶段赋值为100.

Class对象获取方式及是否引起初始化

  • Class clazz1 = 类名.class 没有完成初始化过程.
  • Class clazz2 = 类名.class.getClassLoader().loadClass("全限定名");没有完成初始化过程.
  • Class clazz3 = Class.forName("全限定名")完成初始化过程.
  • Class clazz4 = 对象引用.getClass(); 对象存在了,完成了初始化过程.

四种方式的不同点为有没有初始化。

脑图

大佬,看都看到这了,不关注一个再走🍻🍻 真心感谢关注的哥们