1. 概述

简单来说,static final 变量,也称为常量,是 Java 中创建初始化后不会改变的类变量的关键特性。 但在涉及 static final 对象引用的情况下,对象的状态可能会变化。

在这个教程中,我们将学习如何声明和初始化常量变量,并讨论它们的用途。

2. static final 变量

static 关键字将一个变量关联到类本身,而不是类的实例。

此外,final 关键字使变量变得不可变,初始化后其值不能更改。

这两个关键字的结合有助于创建常量。它们通常使用大写字母和下划线分隔单词命名

2.1. 初始化 static final 变量

下面是如何声明和初始化 static final 字段的例子:

class Bike {
    public static final int TIRE = 2;
}

在这里,我们创建了一个名为 Bike 的类,有一个名为 TIRE 的常量类变量并将其初始化为2。

另一种方式是通过 static 初始化块来初始化变量:

public static final int PEDAL;
static {
    PEDAL = 5;
}

这段代码编译没有错误:

@Test
void givenPedalConstantSetByStaticBlock_whenGetPedal_thenReturnFive() {
    assertEquals(5, Bike.PEDAL);
}

对于常量变量,有一些关键规则:

  • 必须在声明时或在 static 初始化块中初始化
  • 初始化后不能重新赋值

尝试在初始化范围之外初始化它会导致异常。

此外,我们不能通过构造函数初始化它,因为构造函数在创建类的实例时被调用。静态变量属于类本身,而不是单个实例

2.2. static final 对象

我们也可以创建 static final 对象引用:

public static final HashMap<String, Integer> PART = new HashMap<>();

由于 PART 引用是常量,不能重新赋值:

PART = new HashMap<>();

上述代码会抛出异常,因为我们试图将一个新的引用赋给一个不可变的变量。

然而,我们可以修改对象的状态:

@Test
void givenPartConstantObject_whenObjectStateChanged_thenCorrect() {
    Bike.PART.put("seat", 1);
    assertEquals(1, Bike.PART.get("seat"));
    Bike.PART.put("seat", 5);
    assertEquals(5, Bike.PART.get("seat"));
}

在这里,尽管最初设置为1,但我们仍然可以改变 seat 的值。尽管 PART 是一个常量引用,但我们仍然可以修改其内容。只有引用本身是不可变的。

值得注意的是,final 关键字仅使基本类型、String 和其他不可变类型常量化。对于对象,它仅使引用常量,但对象的状态可以被改变

3. 常量为何有用

使用 static final 变量有多个优点。它提供了更好的性能,因为其值在编译时内联,而不是在运行时查找

此外,将可重用的值声明为常量可以避免重复使用字面量。常量可以根据访问修饰符在代码的任何地方重用。具有 private 访问修饰符的常量只能在类内部使用。

另外,基本类型或 String 类型的 static final 变量是线程安全的。当在多个线程之间共享时,其值保持不变。

最后,给常量值赋予语义名称可以提高代码可读性。同时,它使代码自我说明。例如,java.math 包提供了如 PI 这样的常量:

@Test
void givenMathClass_whenAccessingPiConstant_thenVerifyPiValueIsCorrect() {
    assertEquals(3.141592653589793, Math.PI);
}

Math.PI 以可重用的方式封装了数学常量值。

4. 总结

在这篇文章中,我们了解了如何声明和初始化常量变量,并强调了一些用例。

一个 final static 变量定义了一个类级别的常量。然而,即使引用不能改变,static final 对象仍然可能具有可变性。

如往常一样,示例的完整源代码可在 GitHub 上找到。