学习目标
- 最终类和最终方法
- 抽象类和抽象方法
- 接口描述
- 类的自动加载
- 对象克隆和对象遍历
- 单例设计模式
最终类和最终方法
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.php
、UserController.class.php
、UserModel.class.php
。
3、类的自定义加载函数:spl_autoload_register()
- PHP7以下版本,使用__autoload()实现类的自动加载;
- 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);
对象遍历
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) { }