Menu
CoddyTech

final 关键字

本课属于 Coddy Java 学习旅程的“面向对象编程”单元。第 13 课,共 87 课。

Java 中的 final 关键字可以防止修改。它可用于变量、方法和类,并分别产生不同的效果。

将 final 用于变量时,它会使变量成为常量。赋值后,它的值就不能更改:

public class Circle {
    private final double PI = 3.14159;
    private final double radius;
    
    public Circle(double radius) {
        this.radius = radius;  // 可以在构造函数中赋值一次
    }
    
    public void setRadius(double r) {
        // this.radius = r;  // 错误!不能给 final 变量重新赋值
    }
}

一个 final 变量必须在声明时或构造函数中初始化。之后,任何重新赋值的尝试都会导致编译错误。

对于引用类型,final表示引用不能指向其他对象,但对象的内容仍然可以改变:

private final StringBuilder name = new StringBuilder("John");

// name = new StringBuilder("Jane");  // 错误!不能重新赋值
name.append(" Doe");  // 没问题!对象的内容可以改变

对字段使用 final 可以清楚地表达意图,告诉其他开发者,这个值在初始化后不应再改变。这对于配置值、ID 或任何必须在对象的整个生命周期内保持不变的数据尤其有用。

final 方法不能在子类中被重写:

class Base {
    public final void greet() {
        System.out.println("Hi");
    }
}
class Child extends Base {
    // public void greet() { }  // 错误!Base 中的 greet() 是 final 方法
}

final 类完全不能被扩展。String 是一个 final 类,因此没有任何类可以扩展它:

final class Config { }
// class MyConfig extends Config { }  // 错误!Config 是 final 类
challenge icon

挑战

简单

我们来构建一个 Student 注册系统,使用 final 关键字保护学生注册后不应更改的数据。

你将创建两个文件来组织代码:

  • Student.java:创建一个 Student 类,其中某些信息设置后将永久锁定:
    • 一个 final 字段 studentId(String),创建时赋值,之后永远不能更改
    • 一个 final 字段 enrollmentYear(int),表示学生入学的年份,也会永久保留
    • 一个普通的私有字段 name(String),如果学生更改姓名,可以更新此字段
    • 一个接收全部三个值并初始化字段的构造函数
    • 一个用于获取学生 ID 的 getter getStudentId()
    • 一个用于获取入学年份的 getter getEnrollmentYear()
    • 一个用于获取姓名的 getter getName() 和一个用于设置姓名的 setter setName()
    • 一个返回以下内容的方法 getInfo():"ID: [studentId] | Name: [name] | Enrolled: [enrollmentYear]"
  • Main.java:创建一个 Student 对象,然后更新其姓名并显示其信息。你将收到 4 个输入:学生 ID、初始姓名、入学年份,以及要更新成的新姓名。打印两行:
    • 姓名更改前的学生信息
    • 姓名更改后的学生信息

你将依次收到 4 个输入:studentId(String)、name(String)、enrollmentYear(int)和 newName(String)。

注意,final 字段会保护学生的 ID 和入学年份,这些都是不应更改的永久记录;姓名则可以通过 setter 更新。

亲手试试

import java.util.Scanner;

class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        
        // 读取输入
        String studentId = scanner.nextLine();
        String name = scanner.nextLine();
        int enrollmentYear = scanner.nextInt();
        scanner.nextLine(); // 读取换行符
        String newName = scanner.nextLine();
        
        // TODO: 用初始值创建一个 Student 对象
        
        // TODO: 在更改姓名前输出学生信息
        
        // TODO: 使用 setter 更新学生的姓名
        
        // TODO: 在更改姓名后输出学生信息
    }
}
quiz icon自我测试

这节课包含一个小测验。开始学习即可作答,并记录你的进度。

面向对象编程中的所有课

自己练习: Java 在线编译器