final变量需声明时或构造中初始化,保证引用/基本值不可变;static final为类级编译时常量,而final不保证对象内容不可变,仅引用不可变。

final 关键字用于声明不可重新赋值的变量,但要真正实现“运行中不可修改的常量”,需同时满足两个条件:变量被 final 修饰,且其值在初始化后不再改变。Java 中没有真正的“编译期常量”语义(如 C 的 #define),但通过 final + 编译期可确定的值,可让 JVM 将其视为常量(即“编译时常量”)。
基本用法:声明 final 变量
使用 final 修饰变量后,只能赋值一次,之后不能再修改引用或基本类型值:
- 基本类型(如 int、double):值本身不可变;
- 引用类型(如 String、ArrayList):引用地址不可变,但对象内部状态可能仍可修改(除非该类本身是不可变的,如 String)。
正确初始化方式
final 变量必须在声明时、构造器中或实例/静态初始化块中完成初始化,否则编译报错:
-
静态常量(推荐用大写+下划线命名):
public static final int MAX_RETRY = 3;
这是典型的编译时常量,JVM 可能直接内联该值。 -
实例常量(只读字段):
private final String id;
必须在构造方法中赋值:this.id = UUID.randomUUID().toString();
注意:final ≠ 对象内容不可变
final 保证的是“引用不变”,不是“对象不可变”。例如:
-
final List<string> list = new ArrayList();</string>✅ 引用不能指向别的 List;
但list.add("hello");❌ 是完全允许的 —— 因为 ArrayList 本身是可变的。 - 若要确保内容也不变,应使用不可变集合:
final List<string> safeList = List.of("a", "b");</string>(Java 9+)
或Collections.unmodifiableList(...)。
与 static final 的区别
单独用 final(无 static)表示每个对象有自己的只读字段;加 static final 才是类级别的常量,所有实例共享,且通常用于定义全局配置、数学常量等:
-
final double pi = 3.14159;→ 每个对象都有一份副本,不可改; -
static final double PI = 3.1415926535;→ 全局唯一,推荐写法,符合常量命名规范。











