final成员变量是实例级不可变字段,必须在声明时或构造器中初始化;子类构造器须先调用父类构造器确保其初始化,且不可重新赋值;若需子类不同常量值,可通过构造参数传入。

在 Java 继承中,final 修饰的成员变量一旦被初始化,就不能再被修改,从而成为“常量”。但要注意:它不是编译期常量(除非是 static final 基本类型或字符串字面量),而是**实例级不可变字段**。关键在于正确初始化时机和位置。
final 成员变量必须在声明时或构造器中完成初始化
继承关系下,子类不能绕过父类对 final 字段的初始化要求。如果父类有 final 成员变量,它必须在父类的构造器中被赋值(或声明时直接初始化);子类构造器必须先调用父类构造器(隐式或显式 super(...)),确保该 final 字段已被初始化。
- ✅ 正确示例(父类中声明并初始化):
final int MAX_SIZE = 100;
final String NAME;
Parent(String name) {
this.NAME = name; // 在构造器中初始化 final 字段
}
}
class Child extends Parent {
Child() {
super("child"); // 必须调用父类构造器完成 final 初始化
}
}
子类不能重新赋值父类的 final 成员变量
子类可以访问父类的 final 成员变量(只要访问权限允许),但不能修改其值——编译器会报错。
你接手一个需求时,是不是经常这样:"改一个小字段 → 结果把不相关的代码也改了"、"需求说得很模糊 → 做着做着发现跑偏了"、"数据库加个字段 → 上线才发现漏了数据兼容"、"做了个新功能 → 一个月后自己都忘了当时怎么设计的"、"写到一半被中断 → 再回来不知道从哪继续";这个 Skill 就是专门解决这些问题的。 它会自动探测你的项目环境(JDK、Spring Boot、ORM、数据库、前端框架),记在
- ❌ 错误写法(编译不通过):
Child() {
super("child");
// this.MAX_SIZE = 200; // 编译错误:cannot assign a value to final variable
}
}
若需每个子类有不同“常量值”,可用 final + 构造器参数或模板方法
父类定义 final 字段,子类通过构造器传入不同值,实现“每个实例有自己不可变的常量”。
- 推荐模式:
protected final String type;
Shape(String type) {
this.type = type;
}
}
class Circle extends Shape {
Circle() {
super("circle"); // type 被设为 "circle",之后不可变
}
}
class Rectangle extends Shape {
Rectangle() {
super("rectangle"); // type 被设为 "rectangle"
}
}
注意 static final 和普通 final 的区别
如果希望常量属于类而非实例(即所有对象共享、且编译期可优化),应使用 static final。它在类加载时初始化,与继承无关,子类可直接继承或隐藏(不推荐)。
-
public static final double PI = 3.14159;—— 典型类常量,子类可直接用Parent.PI - 普通
final字段属于每个对象,值可因构造参数不同而异,适合表示对象固有不可变属性(如 ID、创建时间、配置名等)
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










