1. 概述

在这个教程中,我们将学习静态块和实例初始化块的概念。我们还将比较和检查类的构造器执行顺序和初始化块。

2. 静态块

在Java中,静态块在对象初始化之前执行代码。静态块是带有static关键字的代码段:

static {
    // definition of the static block
}

静态初始化块或静态初始化段,或者称为静态条款,是静态块的其他名称。静态块的代码仅在类加载期间执行一次。由于编译器在类加载时将它们存储到内存中,并且在对象创建之前,因此静态块始终先于Java中的main()方法执行。

一个类可以有多个静态块,它们的执行顺序与它们在类中出现的顺序相同:

public class StaticBlockExample {

    static {
        System.out.println("static block 1");
    }
    
    static {
        System.out.println("static block 2");
    }

    public static void main(String[] args) {
        System.out.println("Main Method");
    }
}

上述代码片段的输出为:

static block 1
static block 2
Main Method

在这里,编译器首先执行所有静态块,然后调用main()方法。Java编译器确保静态初始化块的执行顺序与源代码中它们出现的顺序相同。

因为编译器先加载父类再加载子类,所以父类的静态块总是先执行。

有趣的是,在Java 1.7之前,每个Java应用程序的main()方法并不是必需的,所以所有的代码都可以写在静态块中。但从Java 1.7开始,main()方法是强制的。

3. 实例初始化块

顾名思义,实例初始化块的目的是初始化实例数据成员

实例初始化块看起来就像静态初始化块,但没有static关键字:

{
     // definition of the Instance initialization block
}

静态初始化块始终在实例初始化块之前执行,因为静态块在类加载时运行。然而,实例块在实例创建时运行。Java编译器将初始化块复制到每个构造器中,因此多个构造器可以使用此方法共享代码段:

public class InstanceBlockExample {

    {
        System.out.println("Instance initializer block 1");
    }
    
    {
        System.out.println("Instance initializer block 2");
    }
    
    public InstanceBlockExample() {
        System.out.println("Class constructor");
    }

    public static void main(String[] args) {
        InstanceBlockExample iib = new InstanceBlockExample();
        System.out.println("Main Method");
    }
}

因此,上述代码的输出将是:

Instance initializer block 1
Instance initializer block 2
Class constructor
Main Method

每次构造器被调用时,都会执行实例初始化块,因为编译器会在构造器本身中复制初始化块。

编译器在执行当前类的实例块之前,会先执行父类的实例块。编译器通过super()调用父类构造器,而实例块在构造器调用时执行。

4. 静态块与实例初始化块的区别

静态块

实例初始化块

它在类加载时执行

它在类实例化时执行

它只能使用静态变量

它可以使用静态或非静态(实例变量)

它不能使用this

它可以使用this

它在整个程序执行期间只执行一次,当类加载到内存中时

每当调用构造器时,它可能会多次运行

5. 总结

在这个教程中,我们了解到编译器在类加载时执行静态块。静态块可用于初始化静态变量或调用静态方法。然而,实例块在每次创建类的实例时执行,可用于初始化实例数据成员。

此外,本文的完整代码示例可以在GitHub上找到。