Menu
CoddyTech

深入了解访问修饰符

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

现在你已经了解了三种访问修饰符,接下来我们来看看它们在继承场景中的行为。关键是,子类可以访问 protected 成员,而 private 成员仍然对它们隐藏,即使是对子类也一样。

<?php
class Vehicle {
    public $brand;
    protected $engineType;
    private $serialNumber;
    
    public function __construct($brand, $engine, $serial) {
        $this->brand = $brand;
        $this->engineType = $engine;
        $this->serialNumber = $serial;
    }
    
    private function getSerial() {
        return $this->serialNumber;
    }
    
    protected function getEngineInfo() {
        return $this->engineType;
    }
}

class Car extends Vehicle {
    public function getDetails() {
        return $this->brand . " - " . $this->getEngineInfo();
        // 访问 $this->serialNumber 会报错,因为它是私有属性!
        // 调用 $this->getSerial() 也会失败,因为它是私有方法!
    }
}

$car = new Car("Toyota", "V6", "ABC123");
echo $car->getDetails();

输出:

Toyota - V6

Car 类可以访问 $brand(public),也可以调用 getEngineInfo()(protected),但无法访问 $serialNumber 或 getSerial(),因为它们是 Vehicle 的私有内容。这种区别让你可以与子类共享某些内部细节,同时将其他内容完全隐藏起来。

要点:当子类需要访问内部数据或方法时,使用 protected;当某些实现细节连子类也不应访问时,使用 private。

challenge icon

挑战

简单

我们来构建一个设备管理系统,看看访问修饰符在继承中的表现。你将创建一个基础 Device 类,其中的属性具有不同的可见性级别,然后通过扩展它来创建 Smartphone 类,展示子类可以和不可以访问哪些成员。

你将把代码组织在三个文件中:

  • Device.php:创建一个 Device 类,用来表示数据敏感级别不同的电子设备。包含:
    • 一个 public 属性 $brand,用于存储制造商名称
    • 一个 protected 属性 $modelNumber,用于内部标识
    • 一个 private 属性 $manufacturingCode,用于存储仅供工厂使用的数据
    构造函数应接收这三个值。添加一个 private 方法 getManufacturingCode(),用于返回制造代码。添加一个 public 方法 getModelInfo(),用于返回 "Model: [modelNumber]"。最后,添加一个 public 方法 getFullCode(),用于返回 "[brand]-[manufacturingCode]"。这展示了类本身如何访问自己的私有成员。
  • Smartphone.php:创建一个继承自 Device 的 Smartphone 类。引入 Device 文件。构造函数应接收品牌、modelNumber、manufacturingCode 和操作系统名称。用前三个值调用父类构造函数,然后将操作系统存储在一个私有属性中。添加一个公共方法 getDeviceDetails(),用于返回 "[brand] [modelNumber] running [os]"。注意,你可以直接访问公共属性 $brand 和受保护属性 $modelNumber,但不能直接访问私有属性 $manufacturingCode:你需要改用继承而来的公共方法 getFullCode()。
  • main.php:引入 Smartphone 文件。你将收到四个输入:品牌名称、型号、制造代码和操作系统。用这些值创建一个 Smartphone 实例。打印三行:
    • 调用 getDeviceDetails() 的结果
    • 调用继承而来的 getModelInfo() 方法的结果
    • 调用继承而来的 getFullCode() 方法的结果

这个挑战重点展示了两者的区别:你的 Smartphone 类可以自由使用从 Device 继承而来的受保护属性 $modelNumber,但私有属性 $manufacturingCode 和方法 getManufacturingCode() 完全不可见:只能通过 Device 选择公开的公共接口访问。

亲手试试

<?php
require_once 'Smartphone.php';

// 读取输入
$brand = trim(fgets(STDIN));
$modelNumber = trim(fgets(STDIN));
$manufacturingCode = trim(fgets(STDIN));
$os = trim(fgets(STDIN));

// TODO:用输入的值创建一个 Smartphone 实例

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

// TODO:打印继承的 getModelInfo() 方法的结果

// TODO:打印继承的 getFullCode() 方法的结果
?>
quiz icon自我测试

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

面向对象编程中的所有课

自己练习: PHP 在线编译器