类的加载连接与初始化

  • 加载
    查找并加载类的二进制数据

  • 连接
    -验证:确保被加载类的正确性
    -准备:为类的静态变量分配内存,并先将其初始化为默认值
    -解析:把类中的符号引用转换为直接引用

  • 初始化
    为类的静态变量赋予正确的初始值

类的加载
类的加载是指将类的.class文件中的二进制数据加载到内存中,将其放在运行时数据区中的方法区内,然后在内存中创建一个Class对象(规范并未说明Class对象放在哪里,HOtSpot虚拟机将其放在了方法区中)用来封装类在方法区内的数据结构。所以说,Class对象是.class文件在内存中的一面镜子。类的加载最终产物就是Class对象

Java程序对类的使用方式可分为两种:主动使用被动使用
Java虚拟机必须在每个类或接口被Java程序首次主动使用才初始化。

主动使用

  1. 创建类的实例
  2. 访问类或接口的静态变量,或者对该静态变量赋值
  3. 调用类的静态方法
  4. 反射(如Class.forName(“com.xxx”))
  5. 初始化一个类的子类

一般除了以上情况,其它方式都被看作是对类的被动使用,不会导致类的初始化

类的加载连接与初始化
其实这里并没有主动使用子类,虽然说用到了MyChild1,但是str是定义在父类的变量。会先执行父类静态代码块,然后在输出变量str。

类的加载连接与初始化
对于静态字段来说,只有直接定义了该字段的类才会被初始化;当一个类在初始化时,要求其父类全部都已经初始化完毕。所以这里是主动使用了子类,会先执行父类的静态代码块,在执行子类的静态代码块,最后输出子类的静态变量。

关于准备阶段和初始化阶段
类的加载连接与初始化
输出结果:counter1: 1,counter2: 0
分析:main方法调用了Singleton的静态方法getInstance,这是对Singleton类的主动使用,会触发其初始化。在Singleton类的准备阶段,会对静态变量赋予默认值,所以这时counter1的值是0,singleton的值null,counter2的值是0。然后进行初始化过程,初始化过程是按照代码顺序逐行进行,在为变量singleton赋值的时候,调用了Singleton的构造方法,这时counter1的值是1,counter2的值是1;接下来完成变量counter2的赋值,由于程序显式赋值0,所以最后counter2的值又变成0了。

类的加载连接与初始化
类的加载连接与初始化
静态变量的声明语句,以及静态代码块都被看做类的初始化语句。