学习目标

  • 最终类和最终方法
  • 抽象类和抽象方法
  • 接口描述
  • 类的自动加载
  • 对象克隆和对象遍历
  • 单例设计模式

最终类和最终方法

1、概述

  • Final关键字修饰的类,就是最终类;
  • Final关键字修饰的方法,就是最终方法;
  • 最终类:该类只能实例化,不能被继承。该类十分完善了,不需要升级和扩展;
  • 最终方法:该方法可以被继承,但不能重写。该方法十分完善了,不需要重写了。

由上可知:最终类和最终方法不能同时使用!!!

2、实例:最终类和最终方法演示

<?php
// 定义一个学生最终类
final class Student
{
}

// 定义一个班级小组学生类
class GroupStu extends Student
{
}

最终类的继承错误
最终类的继承错误

<?php
// 定义一个学生最终类
class Student
{
    // 定义一个最终方法
    final public function demo()
    {
    }
}

// 定义一个班级小组学生类
class GroupStu extends Student
{
    // 重写最终方法
    public function demo()
    {
    }
}

最终方法的重写错误示例
最终方法的重写错误示例

提示:类是我们自己设计的,我们怎么设计,他们就怎么用。

抽象类和抽象方法

1、概述

  • abstract关键字修饰的类,就是抽象类。
  • abstract关键字修饰的方法,就是抽象方法。
  • 抽象类:该类不能直接实例化,必须先继承后再实例化。常用在基础类。
  • 抽象方法:方法必须先继承后重写。
  • 抽象方法就是方法的命名规范、命名规则、方法大纲,也可以理解为一种监督机制。
  • 所有的抽象方法都必须重写,少一个都不行。
  • 抽象方法没有方法体,必须在子类重写后,再定义方法体。
  • 如果一类中有一个抽象方法,该类必须是抽象类。
  • 抽象方法权限不能是private,因为要先继承再重写。
  • PHP7中,抽象方法可以是成员方法,也可以是静态方法。
  • 抽象类中,可以包含其它成员:常量、成员属性、成员方法、静态属性、静态方法。

2、实例:抽象类和抽象方法实例演示

<?php
// 定义一个抽象学生
abstract class Student
{
    // 定义一个最终方法
    final public function demo()
    {
    }
}

// 实例化类
$stu_01 = new Student();

实例化抽象类出错
实例化抽象类出错

<?php
// 定义一个抽象学生
abstract class Student
{
    // 定义一个抽象方法
    // 抽象方法里面只能写参数,不能写函数体,并且在继承类中必须重写!!!
    abstract public function demo();
}

class GroupStu extends Student
{
    public function demo()
    {
        
    }
}

抽象方法里面只能写参数,不能写函数体,并且在继承类中必须必须必须重写!!!

抽象方法
抽象方法

3、练习

<?php
// 定义一个抽象学生类
abstract class Student
{
    const TITLE = '313班级学生类';

    // 定义一个抽象方法
    abstract public function print_info();

    // 定义一个抽象方法
    abstract static function show_title();
}

class GroupStu extends Student
{
    // 
    public function __construct($name, $age)
    {
        $this->name = $name;
        $this->age = $age;
    }

    // 重写方法
    public function print_info()
    {
        return "这位学生{$this->name}的年龄是:{$this->age}"."<br>";
    }

    // 重写方法
    static function show_title()
    {
        return SELF::TITLE."<br>";
    }
}

// 创建对象
$stu_01 = new GroupStu('喜羊羊', 22);

// 调用方法
echo $stu_01->print_info(); // 这位学生喜羊羊的年龄是:22
echo $stu_01->show_title(); // 313班级学生类

接口技术

1、接口的基本概念?

  • 接口就是特殊的抽象类。
  • PHP类是单继承,也就是不支持多继承。
  • 当一个类需要多个类的功能时,单继承就无能为力了,为此PHP引入了类的接口技术。
  • 多人合作开发项目时,需要规范各个功能的名称,就需要用到接口技术。
  • 接口就是一种标准,一种规范。类的功能实现,按照标准接口实现即可。

2、接口定义和实现要点

  • interface关键字定义接口;
  • Implements关键字用来实现接口;
  • 接口中方法权限必须是public
  • 接口中方法默认是抽象的,所以不需要在方法名前面加abstract
  • 接口中方法可以是成员方法,也可以是静态方法;
  • 接口中也可以定义常量,但常量不能重写;
  • 类可以实现(implements)多个接口(相当于把多个功能集于一身,如手机实现了小灵通、MP3、MP4的功能);
  • 接口也可以继承(extends)接口,类可以继承(extends)类,但是类只能实现(implements)接口。

3、实例:接口的定义和实现演示

<?php

// 定义A接口
interface InterfaceA
{
    // 定义常量
    const TITLE = 'A接口';

    // 定义方法
    public function print_info();
}

// 定义B接口
interface InterfaceB
{
    // 定义常量
    // const TITLE = 'B接口'; // 与接口A的常量重名,不能用于同一个类的连接
    const TITLE_B = 'B接口';

    // 定义方法
    public function show_title();
}

class Student implements InterfaceA, InterfaceB
{
    // 
    public function __construct($name, $age)
    {
        $this->name = $name;
        $this->age = $age;
    }
    // 重写接口A的方法
    public function print_info()
    {
        return "{$this->name}的年龄是{$this->age}<br>";
    }

    // 重写接口A的方法
    public function show_title()
    {
        return "大标题是:".SELF::TITLE."<br>";
    }

}

$stu_01 = new Student('张三', '18');

echo $stu_01->print_info(); // 张三的年龄是18
echo $stu_01->show_title(); // 大标题是:A接口

4、实例:创建手机类并实现小灵通接口、MP3接口、MP4接口

<?php

// 定义一个早期手机接口
interface EarlyPhone
{
    // 定义一个打电话方法
    public function call_person($tel);
}

// 定义一个MP3接口
interface Mp3
{
    // 定义一个音频播放方法
    public function play_audio($file_name);
}

// 定义一个MP4接口
interface Mp4
{
    // 定义一个视频播放方法
    public function play_video($file_name);
}

// 定义一个游戏机类
class GameBox
{
    public function __construct($game_arr)
    {
        $this->game_arr = $game_arr;
    }
    public function play_game($game_name)
    {
        if (in_array($game_name, $this->game_arr)) {
            return "正在进行{$game_name}游戏<br>";
        } else {
            return "游戏机内没有{$game_name}游戏<br>";
        }
    }
}

// 创建一个智能手机类
class SmartPhone extends GameBox implements EarlyPhone, Mp3, Mp4
{
    // 重写EarlyPhone类方法
    public function call_person($tel)
    {
        return "正在拨打{$tel}...<br>";
    }
    // 重写Mp3类方法
    public function play_audio($file_name)
    {
        return "正在播放{$file_name}...<br>";
    }
    // 重写Mp4类方法
    public function play_video($file_name)
    {
        return "正在播放{$file_name}...<br>";
    }
}

// 定义游戏列表
$game_arr = ['愤怒的小鸟', '神庙逃亡', '贪吃蛇', '俄罗斯方块'];

// 创建一个windowsphone实例
$windows_phone = new SmartPhone($game_arr);

// 调用打电话方法
echo $windows_phone->call_person(10086); // 正在拨打10086...

// 调用听歌方法
echo $windows_phone->play_audio('不浪漫罪名'); // 正在播放不浪漫罪名...

// 调用视频播放方法
echo $windows_phone->play_video("老友记"); // 正在播放老友记...

// 调用打游戏方法
echo $windows_phone->play_game('愤怒的小鸟'); // 正在进行愤怒的小鸟游戏
echo $windows_phone->play_game('愤怒的大瓜'); // 游戏机内没有愤怒的大瓜游戏

类的自动加载

1、为什么需要类的自动加载

很多开发者写面向对象的应用程序时,对每个类的定义,都建立一个独立的 PHP类文件,方便类文件的统一管理,这无可厚非。但一个很大的烦恼是,不得不在每个脚本开头,写一个长长的包含文件列表(每个类一个文件)。这样一来,就增加了很多负担、占用了很多的内存,对于后期维护也不方便。

解决方案:按需要加载类文件,而不是把所有类全部包含进来

2、类文件的命名规范

  • 一个类要单独定义成一个独立的类文件;
  • 类文件扩展名,要以".class.php"结尾,是一种约定,不是必须的;
  • 类文件主名,要与类名一致;
  • 例如:Db.class.phpUserController.class.phpUserModel.class.php

3、类的自定义加载函数:spl_autoload_register()

  1. PHP7以下版本,使用__autoload()实现类的自动加载;
  2. PHP7以上版本,使用spl_autoload_register()实现类的自动加载。

(1)spl_autoload_register()何时调用?

当试图使用未定义的类时spl_autoload_register自动调用,使用一个类有以下几种情况:

  • 使用new关键字创建不存在类的对象时,spl_autoload_register自动调用;例如:$obj = new Student()
  • 当使用静态化方式访问一个不存在的类时,spl_autoload_register自动调用,例如:Student::show()
  • 当继承一个不存在的类时,spl_autoload_register自动调用,例如:class Stu extends Parent{}
  • 当实现一个不存在的接口时,spl_autoload_register自动调用,例如:class Stu implements Inter

(2)语法格式

  • 描述:将函数注册到SPL(标准PHP库)的__autoload函数队列中。如果该队列中的函数尚未激活,则激活它们。它实际上创建了autoload 函数的队列,按定义时的顺序逐个执行。
  • 语法:

    bool spl_autoload_register ([ callback $autoload_function ] )
  • 参数:$autoload_function,欲注册的自动装载函数,可以是匿名函数,也可以是字符串的函数名称。$autoload_function有一个传递过来的类名形参,用于在函数中构建类文件路径。
  • 返回:成功时返回 TRUE, 或者在失败时返回 FALSE。

(3)使用普通函数作为参数

<?php
// 创建两条规则
spl_autoload_register('rule_01');
spl_autoload_register('rule_02');

// 引入规则1文件
function rule_01($className) 
{
    $file_path = './public/' . $className . '.class.php';
    // echo $file_path;
    // die();
    if (file_exists($file_path)) require_once($file_path);
}

// 引入规则2文件
function rule_02($className)
{
    $file_path = './libs/' . $className . '.cla.php';
    // echo $file_path;
    // die();
    if (file_exists($file_path)) require_once($file_path);
}


// 新建一个学生实例
$stu_01 = new Student();

// 新建一个老师实例
$tea_01 = new Teacher();

举例
举例

(4)使用匿名函数作为参数

// 引入资源
spl_autoload_register(function ($className) {
    // 这个数组中包含了所有可能的路径
    $path_arr = array(
        './public/' . $className . '.class.php',
        './libs/' . $className . '.cla.php'
    );

    foreach ($path_arr as $path) {
        if (file_exists($path)) {
            // 找到路径时,立即退出函数,节省CPU资源
            require_once($path);
            return;
        };
    }
});

对象克隆

1、什么是对象克隆?

  • 如果已存在了一个对象,而还想再创建一个新对象,并且,两个对象的属性值不一样,或者属性比原来多,怎么实现呢? $obj2 = $obj1 无法实现!
  • $obj2 = $obj1,这不是复制对象,而是将$obj1$obj2指向了同一个对象地址。
  • 创建新对象有两种方式:a. 使用new关键字; b. 使用 clone 关键字。

2、实例:对象克隆的演示

真假“复制”
真假“复制”

3、实例:魔术方法__clone()在克隆对象中的使用

魔法方法自动调用。

<?php

// 定义一个学生类
class Student
{
    private $name = '张三';
    public function __clone()
    {
        $this->name = '李四';
    }
}

// 创建一个对象
$stu_01 = new Student;
// 完全复制一个对象
$stu_02 = clone $stu_01;
var_dump($stu_01, $stu_02);

image-20200820212846161
image-20200820212846161

对象遍历

foreach既可以遍历数组元素,也可以遍历对象属性。

注意:类的私有属性在类外遍历时不会出现的,但是在类内是可以的。

在类内外遍历对比
在类内外遍历对比

常用魔术方法

1、__toString()

  • 描述:将对象转成字符串时,__toString()会自动调用。
  • 语法:

    public string __toString ( void )
  • 注意:PHP不支持对象转字符串,因此,不能使用echo输出一个对象。

2、__invoke()

  • 描述:当把一个对象当成函数调用时,__invoke()会自动调用。
  • 语法:

    mixed __invoke ([ $... ] )

面向对象的设计模式

1、什么是对象设计模式?

设计模式(Design pattern)是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性。

2、常用的设计模式有哪些?

  • 单例设计模式:一个类只能创建一个实例对象,不管用什么办法都无法创建第2个对象;
  • 工厂设计模式:生产不同类对象的工厂;
  • 策略设计模式:定义一组算法,将每个算法都封装起来,并且使它们之间可以互换;
  • 观察者设计模式:定义对象间一种一对多的依赖关系,使得每当一个对象改变状态,则所有依赖于它的对象都会得到通知并被自动更新。

3、单例设计模式的要求(三私一公)

  • 一私:私有的静态的保存对象的属性。
  • 一私:私有的构造方法,阻止类外new对象。
  • 一私:私有的克隆方法,阻止类外clone对象。
  • 一公:公共的静态的创建对象的方法。

4、实例:单例设计模式演示

无论页面调用多少次,类都只生成一个对象(永远无法生成第二个对象),且该对象的绝对控制权在类中。

<?php

// 定义一个数据库链接工具类(单例设计类)
class Db
{
    // 定义私有属性
    private $db_host;
    private $db_username;
    private $db_password;
    private $sheet_name;
    private $charset;
    private $sql_link;
    // 定义私有属性实例对象
    private static $obj = null;

    // 定义私有构造方法,防止类外new对象实例
    private function __construct($info_arr = array())
    {
        // 传递变量
        $this->db_host = $info_arr['host'];
        $this->db_username = $info_arr['username'];
        $this->db_password = $info_arr['password'];
        $this->sheet_name = $info_arr['sheet_name'];
        $this->charset = $info_arr['charset'];

        // 启动各个服务
        $this->connect_sql();
        $this->select_sheet();
        $this->set_charset();
    }

    // 定义私有克隆方法,防止克隆对象
    private function __clone()
    {
        return "禁止克隆对象!!!";
    }

    // 定义共有创建对象方法
    public static function create_obj($info_arr)
    {
        if (!(self::$obj instanceof self)) {
            self::$obj = new self($info_arr);
        }
        return self::$obj;
    }

    // 链接数据库的方法
    private function connect_sql()
    {
        if (!($this->sql_link = @mysqli_connect($this->db_host, $this->db_username, $this->db_password))) {
            echo "链接数据库{$this->db_host}失败!!!";
            die();
        }
    }

    // 设置字符集的方法
    private function select_sheet()
    {
        if (!mysqli_select_db($this->sql_link, $this->sheet_name)) {
            echo "链接数据表" . $this->sheet_name . "失败!!!";
            die();
        }
    }

    // 选择数据表
    private function set_charset()
    {
        mysqli_set_charset($this->sql_link, $this->charset);
    }

    // 执行sql语句
    public function do_sql_query($sql_query)
    {
        return mysqli_query($this->sql_link, $sql_query);
    }

    // 公共构析
    public function __destruct()
    {
        mysqli_close($this->sql_link);
    }
}

// 数据库信息
$info_arr = array(
    'host' => 'localhost',
    'username' => 'imageSystem',
    'password' => 'imageSystem',
    'sheet_name' => 'imageSystem',
    'charset' => 'utf8'
);

$db_01 = Db::create_obj($info_arr);
$db_02 = Db::create_obj($info_arr);
var_dump($db_01, $db_02); // object(Student)#1 (0) { } object(Student)#1 (0) { }