Java与C++有一个不同之处在于,Java不但有构造函数,还有一个”初始化块“(Initialization Block)的概念。下面探究一下它的执行顺序与可能的用途。
执行顺序
首先定义A, B, C三个类用作测试,其中B继承了A,C又继承了B,并分别给它们加上静态初始化块、非静态初始化块和构造函数,里面都是一句简单的输出。
主类Main里面也如法炮制。
class A {
static {
System.out.println("Static init A.");
}
{
System.out.println("Instance init A.");
}
A() {
System.out.println("Constructor A.");
}
}
class B extends A {
static {
System.out.println("Static init B.");
}
{
System.out.println("Instance init B.");
}
B() {
System.out.println("Constructor B.");
}
}
class C extends B {
static {
System.out.println("Static init C.");
}
{
System.out.println("Instance init C.");
}
C() {
System.out.println("Constructor C.");
}
}
public class Main {
static {
System.out.println("Static init Main.");
}
{
System.out.println("Instance init Main.");
}
public Main() {
System.out.println("Constructor Main.");
}
public static void main(String[] args) {
C c = new C();
//B b = new B();
}
}
那么可以看到,当程序进入了main函数,并创建了一个类C的对象之后,输出是这样子的:
Static init Main.
Static init A.
Static init B.
Static init C.
Instance init A.
Constructor A.
Instance init B.
Constructor B.
Instance init C.
Constructor C.
观察上面的输出,可以观察到两个有趣的现象:
- Main类是肯定没有被实例化过的,但是由于执行main入口函数用到了Main类,于是static初始化块也被执行了;
- 所有的静态初始化块都优先执行,其次才是非静态的初始化块和构造函数,它们的执行顺序是:
- 父类的静态初始化块
- 子类的静态初始化块
- 父类的初始化块
- 父类的构造函数
- 子类的初始化块
- 子类的构造函数
那么如果有多个实例化对象,又会不会发生变化呢?于是在第一个C类的对象后面,再实例化一个B类的对象,再观察输出:
可以发现这输出跟前面的基本长得一样对吧?只是在后面多了4行,那是新的B类对象实例化时产生的信息,同样也是父类A的初始化块和构造函数先执行,再轮到子类B的初始化块和构造函数执行;同时还发现,静态初始化块的输出只出现了一次,也就是说每个类的静态初始化块都只在第一次实例化该类对象时执行一次。
总结:
- 静态初始化块的优先级最高,也就是最先执行,并且仅在类第一次被加载时执行;
- 非静态初始化块和构造函数后执行,并且在每次生成对象时执行一次;
- 非静态初始化块的代码会在类构造函数之前执行。因此若要使用,应当养成把初始化块写在构造函数之前的习惯,便于调试;
- 静态初始化块既可以用于初始化静态成员变量,也可以执行初始化代码;
- 非静态初始化块可以针对多个重载构造函数进行代码复用。
此博客转载至博客园——BlackStorm,且未经许可不能用于商业目的。如有疑问或授权协商请与原作者联系。