二、面向对象主要特征

1. 构造方法与析构方法

1) 构造方法(__construct)

用于类的初始化,当一个类被初始化时,首先进入的就是类的构造方法,用户初始化一些类的操作

2.析构方法(__destruct)

当一个类的实例被注销时,会调用析构方法,这个方法不是特别常用,但是代表的是一类的实例生命周期的终结.

class Computer
{
    public function __construct()
    {
        echo '构造方法:初始化' . PHP_EOL;
    }

    public function game()
    {
        echo  '玩游戏' . PHP_EOL;
    }

    public function __destruct()
    {
        echo '析构方法:注销操作' . PHP_EOL;
    }
}

$computer = new Computer();
$computer->game();
// 输出结果:
构造方法:初始化  
玩游戏
析构方法:注销操作

上面代码执行结果,体验了一个类从被实例到被注销的过程

当一个 PHP 执行脚本被执行完,其所有类的实例都会被注销,所有都会走 __destruct 函数,下面是一个复杂的例子,在类的实例化时,在构造函数中传入某个私有属性的初始化值,并在后续的函数调用中提现私有属性的意义,最后我们可以看到,两个类的实例在 PHP 脚本执行结束后都被注销.

class Computer
{
    private $memery = 0;
    public function __construct($memery = 0)
    {
        echo '构造方法:初始化' . PHP_EOL;
        $this->memery = $memery;
    }

    public function game()
    {
        if ($this->memery >= 1024) {
            echo '大内存电脑, 可以玩游戏' . PHP_EOL;
        } else {
            echo '小内存电脑,不能玩游戏';
        }
        echo  '玩游戏' . PHP_EOL;
    }

    public function __destruct()
    {
        echo '析构方法:注销操作' . PHP_EOL;
    }
}

$computer1 = new Computer(1024);
$computer1->game();
$computer2 = new Computer(2048);
$computer2->game();
// 输出结果:
构造方法:初始化       
大内存电脑, 可以玩游戏
玩游戏
构造方法:初始化
大内存电脑, 可以玩游戏
玩游戏
析构方法:注销操作
析构方法:注销操作

2. 类的继承与 final 关键字

类的继承关系类似父子关系, 子类继承父类,拥有父类的属性与方法(public 和 protected)

1) 类的继承简介

类的继承好处

  • 扩展性
// 定义父类: Vehicle类
class Vehicle
{
    public function getName()
    {
        echo '交通工具' . PHP_EOL;
    }
}
// 定义子类: Bug 类
class Bus extends Vehicle
{ }

$bus = new Bus();
$bus->getName();
// 输出结果: 交通工具

3) 不同修饰符下属性与方法影响类的继承

上面提到,(public, protected) 修饰的类的属性与方法才能够被子类继承.如果是 private 属性,子类是没有办法继承的

// 定义父类: Vehicle类
class Vehicle
{
    protected function name()
    {
        echo '交通工具' . PHP_EOL;
    }

    // 定义私有方法
    private function author()
    {
        echo 'zZ爱吃菜' . PHP_EOL;
    }
}
// 定义子类: Bug 类
class Bus extends Vehicle
{
    public function getName()
    {
        $this->name();
    }

    public function getAuthor()
    {
        $this->author();
    }
}

$bus = new Bus();
// protected 方法可以被子类继承
$bus->getName();
// 输出结果: 交通工具
// protected 方法无法被外部调用
$bus->name();
// 输出结果: protected 修复的方法不能被外部使用,但可以被子类继承内部使用.
PHP Fatal error:  Uncaught Error: Call to protected method Vehicle::name() from context ''
// private 方法无法被子类继承
$bus->getAuthor();
// 输出结果:
Fatal error: Uncaught Error: Call to private method Vehicle::author() from context 'Bus'

3) 类的属性与方法重写

子类可以覆盖父类方法和属性

// 定义父类: Vehicle类
class Vehicle
{
    public function speed()
    {
        echo '5km/h' . PHP_EOL;
    }
}
// 定义子类: Bug 类
class Bus extends Vehicle
{
	// 重写父类方法 speed
    public function speed()
    {
        echo '80km/h' . PHP_EOL;
    }
}
$bus = new Bus();
$bus->speed();
输出结果: 80km/h

4) 子类实例化过程中

我们知道,类的初始化时会调用构造方法,如果子类初始化时会如何调用构造方法呢?

  • 如果父类定义了构造方法,而子类没有,则调用父类的构造方法
  • 如果父类子类都定义了构造方法,子类重写父类构造方法
  • 子类可以手动调用父类构造方法
// 定义父类: Vehicle类
class Vehicle
{

    public function __construct()
    {
        echo '初始化 Vehicle' . PHP_EOL;
    }
}
// 定义子类: Bug 类
class Bus extends Vehicle
{
    public function __construct()
    {
        parent::__construct();
        echo '初始化 Bus' . PHP_EOL;
    }
}
$bus = new Bus();
// 输出结果:
初始化 Vehicle
初始化 Bus

注意: 一般我们约定类的集成不超过三级,因为继承层数太多,会导致代码难以维护.

5) final 关键字

final 只能修饰 类(class) 和 类的方法(function)

  • final 修饰 class : 表示 这个类不能被继承
  • final 修复 类的方法: 表示这个类的方法不能被重写
// 定义父类: Vehicle类
final class Vehicle
{
    public function __construct()
    {
        echo '初始化 Vehicle' . PHP_EOL;
    }
}
// 定义子类: Bug 类
class Bus extends Vehicle
{}
$bus = new Bus();
// 输出结果: 表示 final class (Vehicle) 不能被继承
PHP Fatal error:  Class Bus may not inherit from final class (Vehicle)

3. 静态绑定

// 定义父类: Vehicle类
class Vehicle
{
    public static function name()
    {
        echo 'Vehicle 的 name 方法' . PHP_EOL;
    }

    public static function getName()
    {
        self::name();
    }
}
// 定义子类: Bug 类
class Bus extends Vehicle
{
    public static function name()
    {
        echo 'Bus 的 name 方法' . PHP_EOL;
    }
}

Bus::getName();
// 输出结果:
Vehicle 的 name 方法

查看上面的代码,我们发现,静态方法 name() Vehicle 与 Bus 都有,但是在调用 Bus::getName(); 时却执行的是 Vehicle 方法.

那么问题来了: 如果我们希望的是 getName() 方法调用的是 Bus 的方法呢?

static 关键字

通过 static 关键字, static::name() 此时 static 就代表调用 who 方法的当前类.也就是后期绑定的概念.

// 定义父类: Vehicle类
class Vehicle
{
    public static function name()
    {
        echo 'Vehicle 的 name 方法' . PHP_EOL;
    }

    public static function getName()
    {
        static::name();
    }
}
// 定义子类: Bug 类
class Bus extends Vehicle
{
    public static function name()
    {
        echo 'Bus 的 name 方法' . PHP_EOL;
    }
}
Bus::getName();
// 输出结果:
Bus 的 name 方法
Last Updated: 8/8/2019, 4:10:24 PM