CodeGym /课程 /JAVA 25 SELF /初始化块

初始化块

JAVA 25 SELF
第 15 级 , 课程 4
可用

1. 什么是初始化块

在 Java 中有两种初始化块:

  • 非静态(实例)初始化块 — 在每次创建新对象时执行,先于构造函数、紧跟在字段初始化之后。
  • 静态初始化块 — 在类被加载进内存时执行一次(先于创建第一个对象或访问静态字段/方法)。

非静态初始化块

直接在类体中声明,不需要使用static 等关键字:

public class User {
    private String name;

    // 非静态初始化块
    {
        System.out.println("正在执行非静态初始化块!");
        name = "默认名称";
    }

    public User() {
        System.out.println("正在执行构造函数!");
    }
}

静态初始化块

使用关键字 static 声明:

public class Config {
    public static String appName;

    static {
        System.out.println("正在执行静态初始化块!");
        appName = "我的超级应用";
    }
}

2. 初始化顺序:谁先谁后?

在 Java 中,类中各元素的初始化顺序不是“随意而为”,而是有严格规定的。如果你曾经不看说明书就装过 IKEA 的家具,你就会明白为什么顺序很重要:如果步骤搞错了,柜子可能会变成装置艺术。

初始化顺序:

  1. 静态字段与静态块 — 按它们在类中声明的先后顺序执行。类加载时只执行一次。
  2. 非静态字段与非静态块 — 按声明顺序,在每次创建新对象时执行。
  3. 构造函数 — 在所有非静态初始化之后执行。

示意图

+-------------------------------+
|   在 JVM 中加载类             |
+-------------------------------+
| 1. 静态字段                   |
| 2. 静态初始化块               |
|         ↓                     |
|   创建对象                    |
|         ↓                     |
| 3. 非静态字段                 |
| 4. 非静态初始化块             |
| 5. 构造函数                   |
+-------------------------------+

带输出的示例

我们来写一个类,直观地展示一切发生的先后顺序:

public class Demo {
    static String staticField = print("1. static 字段");

    static {
        print("2. static 块");
    }

    String field = print("3. 非静态字段");

    {
        print("4. 非静态块");
    }

    public Demo() {
        print("5. 构造函数");
    }

    static String print(String msg) {
        System.out.println(msg);
        return msg;
    }

    public static void main(String[] args) {
        System.out.println("创建第一个 Demo 对象:");
        Demo d1 = new Demo();

        System.out.println("\n创建第二个 Demo 对象:");
        Demo d2 = new Demo();
    }
}

屏幕上会看到什么?

1. static 字段
2. static 块
创建第一个 Demo 对象:
3. 非静态字段
4. 非静态块
5. 构造函数

创建第二个 Demo 对象:
3. 非静态字段
4. 非静态块
5. 构造函数

请注意: 静态部分(static)只会执行一次——在首次访问该类时。所有非 static 的内容,会在每次创建对象时执行。

3. 代码示例:为什么需要初始化块

当构造函数不够用时

有时,部分初始化需要对所有构造函数通用。比如一个类有多个构造函数,而你不想在每个构造函数里都重复相同的初始化代码。此时可以将其提取到非静态初始化块中:

public class Person {
    private String id;
    private String name;

    {
        // 这段代码会在任何构造函数之前执行
        id = java.util.UUID.randomUUID().toString();
        System.out.println("正在生成唯一 id: " + id);
    }

    public Person() {
        System.out.println("Person() 无参数");
    }

    public Person(String name) {
        this.name = name;
        System.out.println("Person(String name)");
    }
}

结果: 创建任何 Person 对象时,会先生成 id,然后再执行相应的构造函数。

初始化复杂的静态数据

静态块常用于初始化“重量级”或复杂的静态字段,例如从文件读取配置、创建集合、连接数据库等。

public class Settings {
    public static final java.util.Map<String, String> DEFAULTS;

    static {
        DEFAULTS = new java.util.HashMap<>();
        DEFAULTS.put("theme", "light");
        DEFAULTS.put("language", "ru");
        System.out.println("Settings 静态块:默认设置");
    }
}

4. 一些有用的细节

何时使用初始化块

适合使用的场景

  • 用于所有构造函数都需要的通用初始化。
  • 用于无法通过简单赋值表达的复杂静态数据。
  • 用于初始化静态资源(例如应用启动时读取配置文件)。

不适合使用的场景

  • 如果可以用普通赋值或构造函数解决——就用它们。
  • 不要把业务逻辑“藏”在初始化块里——这会降低代码的可读性与可维护性。
  • 如果初始化依赖构造函数的参数,请在构造函数中完成。

不要滥用初始化块

初始化块是强大的工具,但并不常用。大多数情况下,普通赋值或构造函数就足够了。如果类里有太多的初始化块,代码会变得难以阅读和维护。

不要用非静态初始化块处理依赖构造参数的逻辑

在非静态初始化块中无法使用构造函数的参数,因为它在构造函数之前执行。如果需要基于参数进行初始化,请在构造函数中完成。

静态初始化块与继承

静态初始化块不会被继承。每个类都有自己的静态块。加载子类时,会先执行基类的静态块,再执行子类的静态块。

5. 使用初始化块的常见错误

错误 №1:期望非静态块能看到构造函数参数。
很多初学者尝试在非静态块中使用构造函数参数,但会得到编译错误或意外结果。请记住:非静态块在构造函数之前执行,也就是说当时还没有任何参数。

错误 №2:在初始化块中放入过多逻辑。
如果初始化块里出现复杂逻辑,代码会变得混乱。最好在构造函数或单独的方法中完成主要工作。

错误 №3:多个静态块的声明顺序不同导致问题。
如果类中有多个静态块,它们会与静态字段一起按代码中的声明顺序执行。如果一个静态块依赖另一个的结果,可能会导致意外结果。

错误 №4:认为静态块会在每次创建对象时执行。
static 块只会执行一次——在类加载时。如果你指望再次初始化,这是不会发生的。

错误 №5:尝试在静态块中访问非静态字段。
在静态块中只能访问静态变量和方法。试图访问非静态(实例)字段会导致编译错误。

1
调查/小测验
封装第 15 级,课程 4
不可用
封装
封装的原则
评论
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION