4.2 final关键字
final = 最终的,不许再改。加在变量前锁"值"、加在方法前锁"重写"、加在类前锁"继承"——三把锁全是给 4.1 的继承踩刹车:改不起的东西,一开始就锁死。
1. 一个词、三把锁
final 是本章学到的第一个"修饰符关键词",它能出现在三种位置,锁的东西完全不同(上面那张图):
| 位置 | 锁住什么 | 效果 |
|---|---|---|
final 变量 |
值 | 赋一次终身不变,再赋编译报错 |
final 方法 |
重写权 | 子类能用这个方法,但不许自己重写一版 |
final 类 |
继承权 | 这个类"断子绝孙",谁也不能 extends 它 |
记忆抓手:final 的英文本意是"最终的"——变量到了最终值、方法到了最终版本、类到了最终形态,都到此为止。
2. 第一把锁:final 变量(最常用)
final int WEEK_DAYS = 7; // 赋一次
WEEK_DAYS = 8; // ❌ 编译报错:cannot assign a value to final variable
注意措辞:不是"运行时改不动",是编译器直接不让过——错误在你按保存键的那一刻就暴露,比 bug 逃到运行时强多了。
两种落点,规矩不同
// ① final 局部变量:声明时就赋值(和 2.2 的局部变量规矩合体)
final double RATE = 0.03;
// ② final 成员变量:声明时、构造方法里,二选一必须赋上
class Circle {
final double PI = 3.14159; // 写法一:声明时直接给
final String id;
public Circle(String id) {
this.id = id; // 写法二:构造方法里给(每个对象可不同)
}
}
普通成员变量有默认值(0/null),final 成员变量连默认值的待遇都没有——必须有人明确赋一次,否则编译不过。逻辑上说得通:"最终值"怎么能是没人定的默认值呢?
static final = 常量标配(2.1 的老朋友正式转正)
public static final double PI = 3.141592653589793;
public static final int MAX_SCORE = 100;
- static(3.7):属于类不属于对象——全类一份
- final(本节):不许改
- 全大写 + 下划线(2.1 的命名软惯例):一看就知道是常量
三个特征凑齐,就是 Java 常量的标准姿势。你天天用的 Math.PI、Integer.MAX_VALUE 全是这个套路——现在你能看懂它们的"配方"了。
什么时候把数提成常量? 代码里出现 0.03 这种"魔法数字"且含义重要时,提成 final double RATE = 0.03;——将来利率变了只改一处,又见 3.3 的"把变化圈在一处"。
3. 第二把锁:final 方法(为 4.3 铺路)
class Animal {
final void breathe() { // 锁住重写权
System.out.println("吸气,呼气");
}
}
class Dog extends Animal {
// ❌ 编译报错:breathe() 不能被重写
// void breathe() { System.out.println("用鳃呼吸"); }
void bark() { ... } // ✅ 自己的新方法随便加
}
两个容易混的点,提前说清:
- final 方法子类照样"白得"、照样能调用——锁的是"换版本",不是"使用权";
- final 方法可以被重载(参数不同就算新方法,2.6 的旧识),只是不能被重写(同名同参数换版本)——重写 vs 重载的完整对决就在下一节,这里先埋个头。
什么时候锁方法? 方法的核心流程不允许子类篡改时。比如一个支付类的 checkPassword(),校验步骤错了就是安全事故——设成 final,子类想动也没门。
4. 第三把锁:final 类
final class String { ... } // Java 官方就是这么定义 String 的
class MyString extends String { } // ❌ 编译报错:无法从最终 String 进行继承
你身边的 final 类比你想象的多:String、Integer、Double、System……全是 final。为什么?这些是 Java 大厦的承重墙——如果谁都能写个 MyString extends String 塞进系统,字符串的可靠性就完了。稳定压倒灵活,是 final 的设计哲学。
final 类里所有方法自动都是"不可重写"的(都没有儿子了,还重写给谁),不用再挨个加 final。
5. 一个进阶小坑:final 锁的是"钥匙",不是"车"
(延续 3.2 的栈钥匙堆实物暗线)
final Dog d = new Dog();
d.age = 3; // ✅ 能改!改的是堆里那只狗
d = new Dog(); // ❌ 报错!钥匙不许再指向别的狗
final 修饰引用类型时,锁的是"引用不能再指别处",不是"对象内容不能改"。类比:final 房产证不许过户,但屋里家具随便挪。这个坑初学阶段记住结论即可,学到第五章对象的更多细节时会回头补一刀。
6. 动手练习(保姆式,接着 IDEA04 项目写)
① 打开 4.1 的 IDEA04 项目,src 右键 → New → Java Class → 输入 Demo01
② 抄下面的代码(四段实验各有编号注释)
③ 绿三角 ▶ 跑,对照"预期输出"逐行核对
public class Demo01 {
// 常量标配:static final + 全大写
public static final int MAX_SCORE = 100;
public static void main(String[] args) {
// 实验① final 局部变量
final int WEEK_DAYS = 7;
System.out.println("一周有 " + WEEK_DAYS + " 天");
// WEEK_DAYS = 8; // 取消注释试试 → 编译报错
// 实验② final 锁钥匙不锁车
final Dog d = new Dog();
d.name = "旺财";
d.name = "小黑"; // ✅ 对象内容随便改
// d = new Dog(); // 取消注释试试 → 编译报错
// 实验③ final 类的成员变量必须在构造里赋值
System.out.println("满分:" + MAX_SCORE);
}
}
final class Certificate { // 实验④ final 类
final String no;
public Certificate(String no) { this.no = no; }
}
// class FakeCert extends Certificate { } // 取消注释试试 → 编译报错
成功标准:正常版跑通两行输出;三处注释各取消一次,亲眼看三种编译报错——final 的三把锁,锁一次记一辈子。
7. 常见误区
成员变量在构造方法里赋值也算数(每个对象定一次终身)。只有局部变量才必须声明时赋值。
反了——子类照样继承、照样调用,只是不许换自己的版本。final 锁的是"改版权",不是"使用权"。
final 锁的是引用(钥匙不许换),对象内部的属性随便改。见第 5 节。
功能上没错,但命名惯例破功:常量必须全大写 + 下划线(MAX_SCORE)。团队代码里小写常量会被当成变量误改——惯例不是装饰,是给读代码的人的信号灯。
微乎其微,别为性能加 final。用 final 的正当理由只有一个:表达"这里不许改"的设计意图,让编译器和读代码的人都帮你盯着。
8. 自测题(先自己答,再展开)
static:属于类,全类一份,类名.MAX 直接访问(3.7);final:赋一次终身不改。合起来 = 类级常量,Java 常量标配三件套的另外两件是"全大写命名"。
不需要。final 类没有子类,"被重写"这件事根本不可能发生——所有方法自动不可重写。
class Circle {
final double PI;
}
final 成员变量 PI 既没在声明时赋值,也没在构造方法里赋值——编译报错"变量 PI 未初始化"。二选一必须占一个。
合法。final 锁的是钥匙 sb 不能再指向别的对象,但堆里那个 StringBuilder 的内容随便改。final 引用 ≠ 不可变对象。
String 是 Java 大厦的承重墙:几乎所有代码都依赖字符串的可靠性。如果不锁,任何人都能 class MyString extends String 篡改行为,全生态的地基就晃了。稳定压倒灵活——这就是 final 的设计哲学。
9. 下一步学什么
- ☑
4.3 抽象类和接口✅ 教材把重写又往后排了:abstract 半成品图纸(不能 new、子类必须兑现)、interface 纯合同(单继承+多实现、自动 public static final)、is-a vs can-do 口诀;方法重写已实战预演,正式规则待下节 - ☐ 复习提示:把本节三处注释实验全做一遍——下一节讲重写时,"final 方法不能重写"就是天然的对照面
相关笔记
- 4.1 继承 ← final 是继承的刹车,先有 extends 才有"锁重写/锁继承"
- 3.7 static关键字 ← static final 常量标配的另一半
- 2.1 Java基本语法 ← 2.1 的"六种常量"是字面值,本节的 static final 是"给字面值起名字"
- 📇 Java基础 · 索引
- 🏠 返回主页