Menu
CoddyTech

final 关键字

本课属于 Coddy PHP 学习旅程的“面向对象编程”单元。第 24 课,共 91 课。

虽然继承和方法重写能提供灵活性,但有时你需要防止某些类或方法被更改。final 关键字可以锁定你的代码,确保子类无法更改特定的实现。

将一个方法标记为 final 后,任何子类都不能重写它:

<?php
class BankAccount {
    protected $balance;
    
    public function __construct($balance) {
        $this->balance = $balance;
    }
    
    final public function getAccountId() {
        return "ACC-" . spl_object_id($this);
    }
}

class SavingsAccount extends BankAccount {
    public function addInterest() {
        return $this->balance * 0.05;
    }
}

$account = new SavingsAccount(1000);
echo $account->getAccountId();

输出:

ACC-1

getAccountId() 方法不能在 SavingsAccount 或任何其他子类中被重写。这样可以保护关键功能,确保所有账户类型都保持一致。

你还可以将整个类标记为 final,以防止任何继承:

<?php
final class SecurityManager {
    public function authenticate($user) {
        return "Authenticating $user";
    }
}

$security = new SecurityManager();
echo $security->authenticate("admin");

输出:

Authenticating admin

没有类可以继承 SecurityManager。这适用于对安全性要求较高的类,也适用于类的设计已经完成且不应再修改的情况。

要点:在方法上使用 final 可以防止方法被重写,同时仍允许继承;在类上使用它则可以完全禁止继承。这是一种保护关键代码的工具,确保代码严格按照设计运行。

challenge icon

挑战

简单

我们来构建一个文档管理系统,演示 final 关键字如何防止子类修改关键功能。

你将创建三个协同工作的文件,用于管理不同类型的文档,同时确保某些安全功能保持锁定:

  • Document.php:定义一个作为所有文档基类的 Document 类。它应有一个公共 $title 属性和一个受保护的 $author 属性。构造函数接受标题和作者。添加一个名为 getSignature() 的 final 方法,该方法返回 "Signed by: [author]"。子类绝不能更改这项安全功能。再添加一个普通的 getContent() 方法,返回 "Document: [title]"。
  • Report.php:定义一个继承 Document 的 Report 类。在文件顶部引入 Document 文件。这个类会添加一个 $department 属性。构造函数应接受标题、作者和部门:前两个参数使用 parent::__construct(),然后设置部门。重写 getContent() 方法,使其返回 "Report: [title] ([department])"。Report 类会继承 final 的 getSignature() 方法,但无法更改它。
  • main.php:引入 Report 文件。你会收到三个输入:文档标题、作者姓名和部门。使用这些值创建一个 Report 对象。在第一行打印 getContent() 的结果,然后在第二行打印 getSignature() 的结果。

这个挑战展示了 final 方法如何保护关键功能:所有文档类型的签名生成方式都保持一致,而 getContent() 等其他方法则可以由各个子类自定义。Report 可以更改内容的显示方式,但签名格式会锁定在父类中。

亲手试试

<?php
require_once 'Report.php';

// 读取输入
$title = trim(fgets(STDIN));
$author = trim(fgets(STDIN));
$department = trim(fgets(STDIN));

// TODO:使用输入值创建一个 Report 对象

// TODO:打印 getContent() 的结果

// TODO:打印 getSignature() 的结果
?>
quiz icon自我测试

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

面向对象编程中的所有课

自己练习: PHP 在线编译器