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-1getAccountId() 方法不能在 SavingsAccount 或任何其他子类中被重写。这样可以保护关键功能,确保所有账户类型都保持一致。
你还可以将整个类标记为 final,以防止任何继承:
<?php
final class SecurityManager {
public function authenticate($user) {
return "Authenticating $user";
}
}
$security = new SecurityManager();
echo $security->authenticate("admin");
输出:
Authenticating admin没有类可以继承 SecurityManager。这适用于对安全性要求较高的类,也适用于类的设计已经完成且不应再修改的情况。
要点:在方法上使用 final 可以防止方法被重写,同时仍允许继承;在类上使用它则可以完全禁止继承。这是一种保护关键代码的工具,确保代码严格按照设计运行。
挑战
简单我们来构建一个文档管理系统,演示 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() 的结果
?>这节课包含一个小测验。开始学习即可作答,并记录你的进度。
面向对象编程中的所有课
自己练习: PHP 在线编译器