一 初始化块和构造器的执行顺序问题一般性规定
1 初始化块总在构造器之前执行,初始化块不能接受形参。
2 用static修饰的初始化块为静态初始化块,由于是静态的,所以是属于类,当类加载时,就执行静态初始化块,但执行一个子类时,最先执行其顶层父类的静态初始化。
3 如果初始化块是属于实例的,只要创建一次对象,初始化块就执行一次。
二 初始化块和构造器综合应用
1 代码示例
class Root { static{ System.out.println("Root的静态初始化块"); } { System.out.println("Root的普通初始化块"); } public Root() { System.out.println("Root的无参数的构造器"); } } class Mid extends Root { static{ System.out.println("Mid的静态初始化块"); } { System.out.println("Mid的普通初始化块"); } public Mid() { System.out.println("Mid的无参数的构造器"); } public Mid(String msg) { // 通过this调用同一类中重载的构造器 this(); System.out.println("Mid的带参数构造器,其参数值:" + msg); } } class Leaf extends Mid { static{ System.out.println("Leaf的静态初始化块"); } { System.out.println("Leaf的普通初始化块"); } public Leaf() { // 通过super调用父类中有一个字符串参数的构造器 super("软件秘笈"); System.out.println("执行Leaf的构造器"); } } public class Test { public static void main(String[] args) { new Leaf(); new Leaf(); } }
2 运行结果
Root的静态初始化块
Mid的静态初始化块
Leaf的静态初始化块
Root的普通初始化块
Root的无参数的构造器
Mid的普通初始化块
Mid的无参数的构造器
Mid的带参数构造器,其参数值:软件秘笈
Leaf的普通初始化块
执行Leaf的构造器
Root的普通初始化块
Root的无参数的构造器
Mid的普通初始化块
Mid的无参数的构造器
Mid的带参数构造器,其参数值:软件秘笈
Leaf的普通初始化块
执行Leaf的构造器
3 结果分析
3.1 第一次创建第一个Leaf对象时,因为系统中还不存在Leaf类,因此需要先加载并初始化Leaf类,初始化Leaf类时会先执行其顶层父类静态初始化,再执行其直接父类的静态初始化块,最后才执行Leaf本身静态初始化块。
3.2 一旦Leaf类初始化成功后,Leaf类在该虛拟机中将一直存在,因此,当第二次创建Leaf实例时无须再对Leaf类进行初始化。
3.3普通初始化块和构造顺序与前面介绍的一致,每次创建一个Leaf对象时,都需先执行最顶层父类的初始化,构造器,然后执行其父类初始化,构造器…最后才执行Leaf类的初始化块和构造器。
三 静态初始化块与指定初始化值的静态成员执行顺序问题
1 代码示例
public class StaticInitTest { // 先执行静态初始化块将a静态成员变量赋值为6 static { a = 6; } // 再将a静态成员变量赋值为9 static int a = 9; public static void main(String[] args) { // 下面代码将输出9 System.out.println(StaticInitTest.a); } }
2 运行结果
9
3 结果分析
当JVM第一次主动使用某个类时,系统会在类准备阶段为该类所有静态成员分配内存,在初始化阶段则负责初始化这些静态成员变量,初始化静态成员变量就是执行类初始化代码或声明类成员变量时的初始值,它们的执行顺序与源代码中的排列顺序相同。