[TOC] #### 1. 什么是抽象类 --- 在 PHP 中,抽象类(Abstract Class)是面向对象编程中用于实现代码复用和制定规范的核心工具。它就像一个 “半成品” 的基类模板,不能被直接拿来使用,而是专门用来给子类继承和扩展的。 抽象类是一种特殊的类,它不能被实例化,只能作为其他类的父类(基类),抽象类中可以包含两种方法: + 抽象方法:只有方法声明,没有具体的实现(方法体),强制要求子类去实现 + 普通方法:有完整的具体实现,子类可以直接继承使用 抽象类与普通类的核心区别: | 特性 | 普通类 | 抽象类 | | ------------ | ------------ | ------------ | | 是否能实例化 | 可以 | 不可以 | | 抽象方法(没有方法体) | 不能包含 | 可以包含 | | 子类继承要求 | 无强制要求 | 必须实现所有抽象方法 | | 使用目的 | 直接创建对象使用 | 作为基类被继承 | 为什么要使用抽象类 ? + 代码复用:将多个子类共有的属性和方法提取到抽象类中,避免重复编写 + 制定规范:通过抽象方法强制约束所有子类必须具备哪些特定的方法,保证代码结构的统一性 + 提高可维护性:当需要修改公共逻辑时,只需要在抽象类中修改一次,所有子类都会自动生效 #### 2. 抽象类的使用 --- 使用抽象类主要遵循:“定义 -\-> 继承 -\-> 实现 -\-> 调用” 的流程 + 定义抽象类与抽象方法:使用 `abstract` 关键字来声明抽象类和抽象方法 + 继承抽象类并实现抽象方法:子类使用 `extends` 关键字继承抽象类。 + 子类必须实现父类中所有的抽象方法,除非子类本身也被声明为抽象类 + 实例化子类并调用方法:虽然抽象类不能实例化,但是继承了抽象类的子类是可以正常实例化使用的 ```php // 抽象类:使用 abstract 关键字定义的类 abstract class Animal { // 抽象方法:只有声明,没有实现(方法体),子类必须实现 abstract public function run(); } // 子类继承抽象类 class Cat extends Animal { // 必须实现抽象类(父类)中的所有抽象方法 public function run() { echo '小猫跑起来了'; } } (new Cat)->run(); ``` #### 3. 抽象方法的特性 --- 抽象方法的定义规则: + 使用 `abstract` 关键字声明 + 没有方法体(不能写 `{}`,直接以分号结束) + 只能存在于抽象类中(普通类不能有抽象方法) + 访问权限通常是 `public` 或 `protected`,不能是 `private`(因为子类必须实现它) 抽象方法与普通方法的差异: | 特性 | 普通方法 | 抽象方法 | | ------------ | ------------ | ------------ | | 是否有方法体 | 必须有 | 不能有 | | 是否必须被重写 | 否,子类可继承也可重写 | 是,子类必须实现 | ```php // 抽象类:使用 abstract 关键字定义 abstract class Animal { // 抽象方法:没有方法体,以分号结尾,子类必须实现 abstract public function run(); // 普通方法:有方法体,子类可以直接调用,也可以重写修改逻辑 public function description() { // 具体实现 } } ``` #### 4. 抽象类使用案例 --- 使用抽象类的核心规则: + 禁止实例化:抽象类只能被继承,不能被实例化,否则会报致命错误 + 强制实现抽象方法:子类必须实现所有抽象方法,除非子类也是抽象类 + 访问控制:子类实现抽象方法时,访问权限必须与父类一致或者更宽松,不能更严格 + 私有方法限制:抽象方法不能声明为 `private`,因为私有方法无法被子类继承和重写 ```php abstract class Animal { protected string $name; // 普通方法:子类可以直接继承使用 public function __construct(string $name) { $this->name = $name; } public function eat() { echo $this->name . "正在吃东西。\n"; } // 抽象方法:强制子类去实现具体的移动方式 abstract public function move(); } class Cat extends Animal { // 必须实现父类的抽象方法 move() public function move() { echo $this->name . "摇摇晃晃的跑起来了。\n"; } } class Dog extends Animal { // 必须实现父类的抽象方法 move() public function move() { echo $this->name . "摇着尾巴向我走来了。\n"; } } // 实例化子类并调用方法 $cat = new Cat('小猫'); $cat->eat(); $cat->move(); $dog = new Dog('小狗'); $dog->eat(); $dog->move(); ``` #### 5. 抽象类的常见误区 --- | 误区 | 正确理解 | | ------------ | ------------ | | 抽象类必须包含抽象方法 | 抽象类可以没有抽象方法,但依然不能被实例化 | | 抽象方法可以有默认实现 | 不能,抽象方法不能有方法体,默认实现写在普通方法中 | | 抽象方法可以是 private | 不能,private 方法子类无法访问,PHP 会报错。必须是 `public` 或 `protected` | 注意点清单: + 抽象类可以有属性、常量、普通方法、抽象方法 + 由于 PHP 的单继承限制,一个类只能继承一个抽象类 + 子类实现抽象方法是,访问级别必须是相同或者更宽松 + 参数签名必须兼容:子类实现的抽象方法参数数量、参数类型必须和父类一致 + 构造方法也可以是抽象的:强制子类实现自己的构造逻辑 + 不要过度使用抽象类:如果多个类只有行为契约没有公共代码,用接口(interface)更合适 + 如果子类不想实现抽象方法,可以将子类也声明为抽象类