final成员变量必须在每个构造方法中且仅赋值一次,赋值须在对象构建完成前、super()之后完成,不可延迟或重复赋值,也不允许在构造器外修改。

在构造方法中初始化 final 成员变量,核心要求是:每个构造方法都必须且只能对该变量赋值一次,并确保在对象完全构建前完成初始化。
必须覆盖所有构造路径
如果类有多个构造器,每个都要显式初始化该 final 字段。编译器不会帮你“继承”或“委托”初始化——哪怕你用 this(...) 调用了另一个构造器,被调用的那个也必须已经完成 final 字段的赋值。
- 允许:两个构造器各自独立赋值
- 错误:某个构造器没赋值,或依赖
this()却未在被调用者中赋值 - 错误:同一个构造器里重复赋值(如先
this()再手动赋)
赋值位置有严格限制
final 字段不能在构造器主体之外赋值,也不能在普通方法、getter/setter 或静态上下文中修改。赋值语句必须出现在构造器代码块内,且通常放在靠前位置(尤其在调用其他实例方法前,避免未初始化就使用)。
- 可在
super()之后、任意逻辑之前赋值 - 支持校验或转换:例如
this.id = Objects.requireNonNull(id, "id required"); - 不可在构造器外(如普通方法)再次赋值,否则编译失败
与实例初始化块的区别
实例初始化块({ ... })虽也能初始化 final 字段,但它会在每个构造器执行前自动插入(位于 super() 之后)。若同时在块和构造器中赋值,会因重复赋值报错。
- 适合多构造器共用的初始化逻辑(如生成 UUID、加载配置)
- 但一旦用了初始化块,构造器中就不能再给同一字段赋值
- 不推荐用于复杂流程——可读性弱,且易与构造器逻辑冲突
不能靠默认值或延迟初始化
final 字段不会获得默认值(如 0、null),编译器强制要求明确初始化。未初始化就访问会导致编译错误,而不是运行时异常。
- 声明时不赋值 → 必须在构造器或初始化块中补上
- 试图在构造器后(如
init()方法)赋值 → 编译不通过 - 局部变量同理:必须在使用前赋值,且仅一次
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











