CodeGym /课程 /JAVA 25 SELF /静态和 final 字段的初始化

静态和 final 字段的初始化

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

1. 静态字段:如何初始化?

静态字段(static)不属于某个对象,而是属于整个类。它的值对该类的所有对象只有一份。打个比方——这就像为公司所有员工设立一只“公共资金箱”:无论来的是谁,大家共用同一个。

在声明处初始化

初始化静态字段最简单、最常见的方式——在声明时直接赋值:

public class User {
    private static int userCount = 0; // 在这里直接初始化

    // ... 其余代码
}

该字段会在类 User 第一次被加载进内存时完成初始化。

在静态代码块中初始化

有时需要更复杂的初始化逻辑——例如,从文件加载数据或执行计算。在这种情况下使用静态初始化块

public class Config {
    public static String configPath;

    static {
        // 该块在类加载时仅执行一次
        configPath = System.getenv("APP_CONFIG_PATH");
        if (configPath == null) {
            configPath = "/etc/app/default.conf";
        }
        System.out.println("Config path 已初始化: " + configPath);
    }
}

静态块何时执行?

  • 首次对类进行访问时(例如创建第一个对象,或调用任何静态方法/字段)。
  • 对于每个类仅执行一次。

访问静态字段的注意事项

  • 应通过类名访问静态字段:User.userCount
  • 也可以通过对象访问,但这被视为不良风格(会让读者困惑)。

示例:

User u1 = new User();
User u2 = new User();
System.out.println(User.userCount); // 正确
System.out.println(u1.userCount);   // 能运行,但不推荐!

2. final 字段:何时以及如何初始化?

final 是一种修饰符,表示:“该字段只能被赋值一次,之后不可更改”。完成初始化后,该字段的值变为不可变:对于对象来说是其固定属性;对于类(如果字段是静态的)来说则是全局常量。

用例:

  • 常量(例如 PI)。
  • 对象的唯一标识,在创建后不可更改。

final 字段的初始化要求

在 Java 中有一条严格规则:每个 final 字段必须在声明处或在类的每一个构造函数中完成初始化

在声明处初始化

public class Circle {
    public static final double PI = 3.1415926535; // 类常量
    private final String id = "CIRCLE";           // 对象常量
}

在构造函数中初始化

有时 final 字段的值只有在创建对象时才确定:

public class User {
    private final int id;

    public User(int id) {
        this.id = id; // 在构造函数中为 final 字段赋值
    }
}

重要:如果类有多个构造函数,final 字段必须在每一个构造函数中都被初始化!

综合示例

public class Token {
    private final String value;
    private final long timestamp;

    public Token(String value) {
        this.value = value;
        this.timestamp = System.currentTimeMillis();
    }
}

初始化不当导致的编译错误

如果忘记初始化 final 字段,编译器将拒绝构建程序:

public class Broken {
    private final int x; // 未初始化

    public Broken() {
        // 未对 x 赋值!
    }
}
// 错误: variable x might not have been initialized

3. static 与 final 的组合:声明类常量

组合 public static final 非常常见。在 Java 中,这样声明类常量——只赋值一次,在程序运行期间不再改变。这些常量属于整个类,对所有对象都相同。

语法与示例

public class MathUtils {
    public static final double PI = 3.1415926535;
    public static final String APP_NAME = "MyApp";
}

说明:

  • public — 到处可见。
  • static — 属于整个类,而非某个对象。
  • final — 初始化后不可更改。

用法

double area = MathUtils.PI * r * r;
System.out.println(MathUtils.APP_NAME);

约定:常量名通常采用全大写并用下划线分隔。

4. 代码示例:静态与 final 字段的多种初始化方式

示例 1:带常量的简单类

public class Constants {
    public static final int DAYS_IN_WEEK = 7;
    public static final String COMPANY = "Romashka LLC";
}

示例 2:在静态代码块中初始化的静态字段

public class AppConfig {
    public static final String DEFAULT_PATH;

    static {
        // 可以执行复杂的逻辑
        String env = System.getenv("APP_PATH");
        if (env != null) {
            DEFAULT_PATH = env;
        } else {
            DEFAULT_PATH = "/usr/local/app";
        }
    }
}

示例 3:对象的 final 字段,在构造函数中初始化

public class User {
    private static int nextId = 1;
    private final int id;
    private String name;

    public User(String name) {
        this.id = nextId++;
        this.name = name;
    }

    public int getId() { 
        return id; 
    }
    public String getName() {
        return name; 
    }
    public void setName(String name) { 
        this.name = name; 
    }
}

用法:

User u1 = new User("Ivan");
User u2 = new User("Maria");
System.out.println(u1.getId()); // 1
System.out.println(u2.getId()); // 2

示例 4:final 字段初始化不当导致的错误

public class BadExample {
    private final int number;

    public BadExample() {
        // number 未初始化!
    }
}
// 编译错误: variable number might not have been initialized

5. 使用静态与 final 字段的最佳实践

仅将 public static final 用于真正的常量

如果该值将来可能改变(例如员工列表),就不要将其设为 final!常量必须真正不可变。

public static final int MAX_USERS = 1000; // 良好
public static final String[] USERS = new String[100]; // 不好!

尽管引用 USERS 本身不会改变,但数组内容仍然可变。这可能导致意想不到的错误。

复杂初始化请使用静态代码块

如果常量无法通过简单赋值表达(例如需要计算或从文件读取),请使用静态块。

不要滥用静态字段

静态字段本质上是全局变量。过多使用会导致难以发现的错误,并增加维护难度。

不要将可变对象声明为 public static final

如果对象是可变的,不要把它做成公开且 final。这样会把其内部状态暴露给所有人,迟早会被某处代码修改。

6. 初始化静态与 final 字段时的常见错误

错误 №1:未初始化的 final 字段。 如果忘记在声明处或所有构造函数中初始化 final 字段,编译器会报错。比如,若类有两个构造函数,而你在其中一个里忘了为 final 字段赋值——就会出错。

错误 №2:修改 final 字段的值。 尝试在初始化之后修改 final 字段会导致编译错误。

public class Demo {
    private final int x = 5;
    public void change() {
        x = 10; // 错误: cannot assign a value to final variable x
    }
}

错误 №3:可变对象的 public static final 字段。 如果将可变对象(String[]int[] 等)声明为 public static final,任何代码都能修改其内容。这会破坏封装,并可能导致难以定位的 bug。

错误 №4:在静态块中使用非静态字段。 在静态块中不能访问非静态字段,因为它们尚未初始化(在该阶段根本不存在)。

错误 №5:出乎意料的初始化顺序。 如果在静态块中访问后面声明的静态字段,它们可能尚未被初始化。若计划在静态块中使用静态字段,请总是先声明它们。

评论
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION