final变量不可再赋值,但引用类型对象内部状态仍可变;必须显式初始化,基本类型值固定,引用类型仅锁定引用;String因类和字段均final而天然不可变。

Java 中用 final 声明变量,不是让“值永远不变”,而是让“这个变量不能再指向别的东西”。基本类型确实值不可改,但引用类型只锁住地址,不锁住对象内部状态——这是绝大多数人踩坑的起点。
final 变量必须初始化,且只能一次
final 变量没有默认值,编译器强制你明确赋值:
- 局部变量:必须在使用前完成初始化(声明时或之后某条语句)
- 实例变量:必须在声明时、实例初始化块中,或每个构造方法结束前完成
- 静态变量(static final):只能在声明时赋值,或在静态初始化块中赋值
基本类型 vs 引用类型:不可变的不是“对象”,是“绑定”
final 对不同类型的约束本质一致,但表现不同:
-
基本类型(如 int、boolean):值本身固定,比如
final int port = 8080;后不能再改 -
引用类型(如 List、StringBuilder):变量不能换对象,但对象内容可修改。例如:
final StringBuilder sb = new StringBuilder("a");sb.append("b"); // ✅ 允许sb = new StringBuilder("c"); // ❌ 编译报错
常见误用:以为 final 就等于“不可变对象”
final 只管变量名和对象的关联关系,不管对象内部是否安全:
立即学习“Java免费学习笔记(深入)”;
-
final List<string> list = new ArrayList();</string>→ list 不能指向新集合,但list.add()完全合法 - 若需真正不可变集合,要用
Collections.unmodifiableList(list)或 Guava 的ImmutableList - String 是个特例:它本身是 final 类 + 所有字段 private final + 内部无修改逻辑 → 天然不可变
final 形参与匿名类中的隐式约束
在方法参数或 Lambda 中使用 final,主要是为了保证线程安全和语义清晰:
public void handle(final String id) { /* id = "new"; ❌ 不允许 */ }- 在 Java 8+ 中,即使不写
final,只要变量未被修改,就视为 effectively final,可被匿名类或 Lambda 捕获 - 显式加
final是一种自我提醒:这个值在此作用域内就是固定的


















