# 面向对象
面向对象编程(OOP)是一种编程范式,它以对象的概念为核心,将数据和操作封装在对象中。在面向对象编程中,程序被组织成彼此交互的对象集合,每个对象都可以包含数据(属性)和操作(方法)。这种方法使得代码更加模块化、可维护性更强,并且更易于理解和重用。
什么是对象?
例如:一台电视机,一辆汽车,一个苹果都是一个对象
小猫名字:aa, 性别:公,花色:橘色;会叫,会吃饭,会打闹
小狗名字:bb, 性别:公,花色:黑色;会叫,会吃饭,会打闹
对象的主要三个特性;
- 对象的行为:对象可以执行的操作,比如:叫,坐下就是行为。
- 对象的形态:对对象不同的行为是如何响应的,比如:颜色,大小,外型。
- 对象的表示:对象的表示就相当于身份证,具体区分在相同的行为与状态下有什么不同。
面向对象编程有以下几个基本概念:
类(Class):类是对象的模板或蓝图,它定义了对象的属性和方法。类可以看作是一种用户自定义的数据类型。
对象(Object):对象是类的实例,它是具体存在的数据结构,包含了类中定义的属性和方法。
封装(Encapsulation):封装是将数据和操作封装在对象中的过程,通过将数据隐藏在对象内部,只暴露有限的接口给外部,实现了数据的保护和安全性。
继承(Inheritance):继承是指一个类(子类)可以继承另一个类(父类)的属性和方法。子类可以通过继承和扩展来重用父类的代码,并且可以添加新的功能。
多态(Polymorphism):多态允许不同类的对象对同一消息做出响应,提供了一种统一的接口以处理不同类的对象
# 类 class
类的定义
public 公有
protected 受保护
private 私有
<?php | |
class Animal{ | |
public $name = "花花"; | |
public $age = 20; | |
public function catname($name){ | |
echo $name . "在吃饭."; | |
} | |
} | |
$cat = new Animal; | |
echo $cat->name; | |
echo $cat->age; | |
echo $cat->catname("小猫"); | |
?> | |
<?php | |
class Site { | |
/* 成员变量 */ | |
public $url; | |
public $title; | |
/* 成员函数 */ | |
function setUrl($par){ | |
$this->url = $par; | |
} | |
function getUrl(){ | |
echo $this->url . PHP_EOL; | |
} | |
function setTitle($par){ | |
$this->title = $par; | |
} | |
function getTitle(){ | |
echo $this->title . PHP_EOL; | |
} | |
} | |
$google = new Site; | |
$taobao = new Site; | |
$feishu = new Site; | |
$google->setUrl("http://www.google.com"); | |
$taobao->setUrl("http://www.taobao.com"); | |
$feishu->setUrl("http://www.feishu.com"); | |
$google->setTitle("谷歌"); | |
$taobao->setTitle("淘宝"); | |
$feishu->setTitle("飞书"); | |
$google->getUrl(); | |
$google->getTitle(); | |
$taobao->getUrl(); | |
$taobao->getTitle(); | |
$feishu->getUrl(); | |
$feishu->getTitle(); | |
?> |
# 类的方法和属性
public $name = "花花"; | |
public $age = 20; | |
#这两个就是属性 | |
#而函数 catname 就是方法 | |
public function catname($name){ | |
echo $name . "在吃饭."; | |
} | |
#在类中定义一个属性要用到 public 或者 var 不可以省略不写,但是定义函数时可以不写 |
# $this
代表自身的对象
<?php | |
class Animal{ | |
public $name = "花花"; | |
public $age = 20; | |
public function catname(){ | |
echo $this->name . "在吃饭."; | |
} | |
} | |
$cat = new Animal; | |
$cat -> name="小环"; | |
echo $cat->catname(); | |
$dog = new Animal; | |
$dog -> name="小花"; | |
echo $dog->catname(); | |
?> | |
#去调用自身的属性name | |
public $name = "花花"; | |
#属性也可以在类外更改,输出就是 | |
$cat -> name="小环"; | |
$dog -> name="小花"; | |
//小环在吃饭.小花在吃饭. |
# PHP 构造函数
构造函数是一种特殊的方法。主要用来在创建对象时初始化对象, 即为对象成员变量赋初始值,在创建对象的语句中与 new 运算符一起使用。创建对象时被调用
<?php | |
class Site { | |
/* 成员变量 */ | |
public $url; | |
public $title; | |
/* 成员函数 */ | |
function getUrl(){ | |
echo $this->url . PHP_EOL; | |
} | |
function getTitle(){ | |
echo $this->title . PHP_EOL; | |
} | |
function __construct( $par1, $par2 ) { | |
$this->title = $par1; | |
$this->url = $par2; | |
} | |
function info() | |
{ | |
echo "网站标题:$this->title 网站连接:$this->url" . PHP_EOL; | |
} | |
} | |
$google = new Site("谷歌","www.google.com"); | |
$taobao = new Site("淘宝","www.taobao.com"); | |
$feishu = new Site("飞书","www.feishu.com"); | |
$google -> info(); | |
$taobao -> info(); | |
$feishu -> info(); | |
?> | |
//输出 | |
网站标题:谷歌 网站连接:www.google.com | |
网站标题:淘宝 网站连接:www.taobao.com | |
网站标题:飞书 网站连接:www.feishu.com |
# 析构函数
析构函数 (destructor) 与构造函数相反,当对象结束其生命周期时(例如对象所在的函数已调用完毕),系统自动执行析构函数。
<?php | |
class MyDestructableClass { | |
public $name; | |
function __construct($par1) { | |
print "构造函数\n"; | |
$this->name = $par1; | |
} | |
function __destruct() { | |
print "销毁 " . $this->name . "\n"; | |
} | |
} | |
$obj = new MyDestructableClass("木马"); | |
?> | |
//输出 | |
构造函数 | |
销毁 木马 | |
$obj执行完毕后将自动执行__destruct |
# 静态变量类常量
static 静态变量,self 输出静态变量
<?php | |
class Animal{ | |
public static $sleep = "去睡觉"; | |
public $name = "花花"; | |
public $age = 20; | |
public function catname(){ | |
echo $this->name . "在吃饭."; | |
} | |
function xingwei(){ | |
echo self::$sleep; | |
} | |
function info2() | |
{ | |
echo $this->name . self::$sleep .PHP_EOL; | |
} | |
} | |
$cat = new Animal; | |
$cat -> name="小猫"; | |
echo $cat->info2(); | |
$dog = new Animal; | |
$dog -> name="小狗"; | |
echo $dog->info2(); | |
// 输出为:小猫去睡觉,小狗去睡觉 | |
?> | |
//如果改变了$sleep这个静态变量,两个对象都会受到影响 | |
<?php | |
class Animal{ | |
public static $sleep = "去睡觉"; | |
public $name = "花花"; | |
public $age = 20; | |
public function catname(){ | |
echo $this->name . "在吃饭."; | |
} | |
function xingwei(){ | |
echo self::$sleep; | |
} | |
function info2() | |
{ | |
echo $this->name . self::$sleep .PHP_EOL; | |
} | |
} | |
$cat = new Animal; | |
Animal::$sleep = "去洗澡"; | |
$cat -> name="小猫"; | |
echo $cat->info2(); | |
$dog = new Animal; | |
$dog -> name="小狗"; | |
echo $dog->info2(); | |
// 输出为小猫去洗澡,小狗去洗澡 | |
?> |
# 类常量
类常量跟静态变量很像但是类常量不能更改
使用场景对象公用一个属性
<!-- 类常量 --> | |
<?php | |
class Animal{ | |
public static $sleep = "去睡觉"; | |
const up = "起床了"; | |
public $name = "花花"; | |
public $age = 20; | |
public function catname(){ | |
echo $this->name . "在吃饭."; | |
} | |
function xingwei(){ | |
echo self::$sleep; | |
} | |
function info2() | |
{ | |
echo $this->name . self::up .PHP_EOL; | |
} | |
} | |
$cat = new Animal; | |
Animal::$sleep = "去洗澡"; | |
$cat -> name="小猫"; | |
echo $cat->info2(); | |
$dog = new Animal; | |
$dog -> name="小狗"; | |
echo $dog->info2(); | |
// 输出小猫起床了,小狗起床了 | |
// 类外调用类常量 | |
echo Animal::up; | |
?> |
# 静态方法
//静态方法格式为和静态变量一样加一个static关键字 | |
public static function ez(){ | |
echo self::$sleep; | |
} | |
//静态方法调用静态方法 | |
<?php | |
class Animal | |
{ | |
public static $sleep = "静态变量"; | |
const up = "类常量"; | |
// 调用类常量 | |
public static function ez(){ | |
echo self::up; | |
echo self::$sleep; | |
} | |
// 调用静态 ez 方法 | |
public static function xingwei(){ | |
echo self::ez(); | |
} | |
} | |
Animal::xingwei();// 输出类常量,静态变量 | |
?> | |
//静态方法调用动态方法和动态变量 | |
<?php | |
class Animal | |
{ | |
public static $sleep = "静态变量"; | |
public $name = "动态变量"; | |
const up = "类常量"; | |
// 调用类常量 | |
public function ez(){ | |
echo self::up; | |
echo self::$sleep; | |
} | |
// 调用动态 ez 方法和动态 name 变量 | |
public static function xingwei(){ | |
echo (new self)->ez(); | |
echo (new self)->name; | |
} | |
} | |
Animal::xingwei();// 输出类常量,静态变量,动态变量 | |
?> |
# 继承
在 PHP 中,一个类可以通过使用 extends 关键字来继承另一个类的方法和属性这是面向对象编程中的一个重要特性,它允许我们在一个已经存在的类的基础上创建新的类,当一个类继承另一个类时,子类会继承父类所有的 public 和 protected 的方法,属性和常量除非子类覆盖了父类的方法,被继承的方法都会保留其原有功能这有助于功能的设计和抽象,在实现类似的对象、增加新功能时,无须重复编写这些公用的功能
需要注意的是,PHP 不支持多重继承,一个类只能继承一个基类此外,子类无法访问父类的私有方法
下面是一个 PHP 类继承的例子
<?php | |
class Anima{ | |
public $name = "动物"; | |
protected $age = 10; | |
private $sex = "雌性"; | |
} | |
class dog extends Anima{ | |
} | |
$dog = new dog; | |
echo $dog->name; | |
echo $dog->age; | |
echo $dog->sex; | |
// 这样运行会报错因为子类不可访问父类的私有方法 private 和私有变量,并且受保护的变量 protected 只能在类中访问不可以 new 出对象后访问 | |
// 继承过来的子类和父类是相同的,但是不一定都可以访问 | |
var_dump(new Anima); | |
var_dump(new dog);//var_dump 只能打印出属性不能打印出方法 | |
动物object(Anima)#5 (3) { | |
["name"]=> | |
string(6) "动物" | |
["age":protected]=> | |
int(10) | |
["sex":"Anima":private]=> | |
string(6) "雌性" | |
} | |
object(dog)#5 (3) { | |
["name"]=> | |
string(6) "动物" | |
["age":protected]=> | |
int(10) | |
["sex":"Anima":private]=> | |
string(6) "雌性" | |
} | |
// 子类也会继承父类的构造函数,子类可以调用父类的方法也可以调用自己写的方法,父类不可调用子类里的方法 | |
<?php | |
class Anima | |
{ | |
public $name = "动物"; | |
protected $age = 10; | |
private $sex = "雌性"; | |
public function __construct($name) | |
{ | |
$this->name = $name; | |
} | |
public function info() | |
{ | |
echo $this->name . "在吃饭". PHP_EOL; | |
} | |
} | |
class dog extends Anima{ | |
public function ja(){ | |
echo $this->name ."在跑" . PHP_EOL; | |
} | |
} | |
$dog = new dog("小狗"); | |
$dog->info(); | |
$dog->ja(); | |
/// 小狗在吃饭 | |
// 小狗在跑 | |
?> | |
<?php | |
// 父类 | |
class Animal { | |
protected $name; | |
public $colour; | |
public $age; | |
public function __construct($name, $colour, $age) { | |
$this->name = $name; | |
$this->colour = $colour; | |
$this->age = $age; | |
} | |
public function getDescription($activity) { | |
return "一只名叫{$this->name}的{$this->colour}小{$activity},它今年{$this->age}岁。"; | |
} | |
} | |
// 子类 | |
class Dog extends Animal { | |
public function __construct($name, $colour, $age) { | |
parent::__construct($name, $colour, $age); | |
} | |
public function getActivity() { | |
return "狗"; | |
} | |
} | |
class Cat extends Animal { | |
public function __construct($name, $colour, $age) { | |
parent::__construct($name, $colour, $age); | |
} | |
public function getActivity() { | |
return "猫"; | |
} | |
} | |
$dog = new Dog("小黑", "黑色", 2); | |
echo $dog->getDescription("狗") . "\n"; | |
$cat = new Cat("小白", "白色", 3); | |
echo $cat->getDescription("猫") . "\n"; | |
?> | |
/parent 是在 PHP 中用来引用父类的关键字。它通常用在子类中,用于调用父类的构造函数、方法或属性。 |
类方法属性重写 final 关键字
//如果对父类继承过来的方法不满意是可以在子类更改的,父类里的方法不受影响 | |
<?php | |
class Anima | |
{ | |
public $name = "动物"; | |
protected $age = 10; | |
private $sex = "雌性"; | |
public function __construct($name) | |
{ | |
$this->name = $name; | |
} | |
public function info() | |
{ | |
echo $this->name . "在吃饭". PHP_EOL; | |
} | |
} | |
class dog extends Anima{ | |
public function ja(){ | |
echo $this->name ."在跑" . PHP_EOL; | |
} | |
public function info() | |
{ | |
echo $this->name . "在睡觉". PHP_EOL; | |
} | |
} | |
$dog = new dog("小狗"); | |
$dog->info(); | |
$dog->ja(); | |
// 小狗在睡觉 | |
// 小狗在跑 | |
?> | |
//final防止重写 | |
<?php | |
class Anima | |
{ | |
public $name = "动物"; | |
protected $age = 10; | |
private $sex = "雌性"; | |
public function __construct($name) | |
{ | |
$this->name = $name; | |
} | |
final public function info() | |
{ | |
echo $this->name . "在吃饭". PHP_EOL; | |
} | |
} | |
class dog extends Anima{ | |
public function ja(){ | |
echo $this->name ."在跑" . PHP_EOL; | |
} | |
public function info() | |
{ | |
echo $this->name . "在睡觉". PHP_EOL; | |
} | |
} | |
$dog = new dog("小狗"); | |
$dog->info(); | |
$dog->ja(); | |
// 小狗在睡觉 | |
// 小狗在跑 | |
?> | |
//在info方法前面加上final将会限制info被重写,就会报致命错误:无法重写中的最终方法Anima::Info |
# parent 调用父类方法和构造函数
格式为parent::方法名 parent::__construct | |
<?php | |
class Anima | |
{ | |
public $name = "动物"; | |
protected $age = 10; | |
private $sex = "雌性"; | |
public function __construct($name) | |
{ | |
$this->name = $name; | |
} | |
public function info2(){ | |
echo $this->name . "在洗澡". PHP_EOL; | |
} | |
public function info() | |
{ | |
echo $this->name . "在吃饭". PHP_EOL; | |
} | |
} | |
class dog extends Anima{ | |
public function ja(){ | |
echo $this->name ."在跑" . PHP_EOL; | |
} | |
public function info() | |
{ | |
parent::info2(); | |
echo $this->name . "在睡觉". PHP_EOL; | |
} | |
} | |
$dog = new dog("小狗"); | |
$dog->info(); | |
//$dog->ja(); | |
// 小狗在睡觉 | |
// 小狗在跑 | |
?> | |
//在info方法中调用了父类的info2方法,打印为小狗在洗澡,小狗在睡觉 | |
//调用父类的构造函数 | |
class Anima | |
{ | |
public $name; | |
protected $age = 10; | |
private $sex = "雌性"; | |
public function __construct($name) | |
{ | |
$this->name = $name; | |
} | |
public function info2(){ | |
echo $this->name . "在洗澡". PHP_EOL; | |
} | |
public function info() | |
{ | |
echo $this->name . "在吃饭". PHP_EOL; | |
} | |
} | |
class dog extends Anima{ | |
public function __construct($name){ | |
echo "dog的构造函数"; | |
} | |
public function ja(){ | |
echo $this->name ."在跑" . PHP_EOL; | |
} | |
public function info() | |
{ | |
parent::info2(); | |
echo $this->name . "在睡觉". PHP_EOL; | |
} | |
} | |
$dog = new dog("小狗"); | |
$dog->info(); | |
//如果是这样写将会输出在吃饭,在睡觉因为dog自己写了构造函数父类的将不会生效$name的参数也就用不到了所以要在dog构造函数里添加parent::__construct($name); | |
//在dog的构造函数中我们也可以添加参数,如果添加了参数new对象的时候没有传参数就会报错,可以给__construct($name,$age,$age2="20")$age2一个默认的参数这样不传入参数就默认是20 | |
<?php | |
class Anima | |
{ | |
public $name; | |
protected $age = 10; | |
private $sex = "雌性"; | |
public function __construct($name) | |
{ | |
$this->name = $name; | |
} | |
public function info2(){ | |
echo $this->name . "在洗澡". PHP_EOL; | |
} | |
public function info() | |
{ | |
echo $this->name . "在吃饭". PHP_EOL; | |
} | |
} | |
class dog extends Anima{ | |
public function __construct($name,$age,$age2="20"){ | |
parent::__construct($name); | |
echo "dog的构造函数" . $age . $age2 . PHP_EOL; | |
} | |
public function ja(){ | |
echo $this->name ."在跑" . PHP_EOL; | |
} | |
public function info() | |
{ | |
parent::info2(); | |
echo $this->name . "在睡觉". PHP_EOL; | |
} | |
} | |
$dog = new dog("小狗","10"); | |
$dog->info(); | |
//$dog->ja(); | |
// 小狗在睡觉 | |
// 小狗在跑 | |
?> |
# 接口
使用接口 (interface),可以指定某个类必须实现哪些方法,但不需要定义这些方法的具体内容。
接口是通过 interface 关键字来定义的,就像定义一个标准的类一样,但其中定义所有的方法都是空的。
接口中定义的所有方法都必须是公有,这是接口的特性。
要实现一个接口,使用 implements 操作符。类中必须实现接口中定义的所有方法,否则会报一个致命错误。类可以实现多个接口,用逗号来分隔多个接口的名称。
<?php | |
interface AnimalInterface2{ | |
public function play(); | |
} | |
interface AnimalInterface{ | |
const up = "起床了"; | |
public function eat(); | |
public function sleep(); | |
public function run(); | |
} | |
class Dog implements AnimalInterface, AnimalInterface2{ | |
public function eat(){ | |
echo "狗在吃"; | |
} | |
public function sleep(){ | |
echo "狗在睡觉"; | |
} | |
public function run(){ | |
echo "狗在跑"; | |
} | |
public function play(){ | |
echo "狗在玩"; | |
} | |
} | |
class Cat implements AnimalInterface{ | |
public function eat(){ | |
echo "猫在吃"; | |
} | |
public function run(){ | |
echo "猫在跑"; | |
} | |
public function sleep() | |
{ | |
echo "猫在睡觉"; | |
} | |
} | |
$dog = new Dog(); | |
$cat = new Cat(); | |
$dog->eat(); | |
$dog->sleep(); | |
$dog->run(); | |
$dog->play(); | |
$cat->run(); | |
$cat->sleep(); | |
$cat->eat(); | |
?> | |
//一个类如果引用了一个接口就必须用到接口里的所有方法不然会报错,而且调用的方法不可以更改必须使用接口里的规范,常量可以不使用,调用两个接口接口名之间用逗号隔开 |
# 抽象类和抽象方法
任何一个类,如果它里面至少有一个方法是被声明为抽象的,那么这个类就必须被声明为抽象的。
定义为抽象的类不能被示例化。
被定义为抽象的方法只是声明了其调用方式(参数),不能定义其具体的功能实现。
继承一个抽象类的时候,子类必须定义父类中的所有抽象方法;另外,这些方法的访问控制必须和父类中一样(或者更为宽松)。例如某个抽象方法被声明为受保护的,那么子类中实现的方法就应该声明为受保护的或者公有的,而不能定义为私有的。
abstract class Animal{}//抽象类的定义 | |
abstract function doSomething();//抽象方法 | |
<!-- 抽象类 --> | |
<?php | |
abstract class Animal{ | |
abstract public function doSomething(); | |
abstract public function bathe(); | |
} | |
class Baby extends Animal{ | |
public function __construct($name){ | |
$this->name = $name; | |
} | |
public function bathe(){ | |
return $this->name . "在洗澡" . PHP_EOL; | |
} | |
public function doSomething(){ | |
return $this->name . "在玩耍" . PHP_EOL; | |
} | |
} | |
$baby = new Baby("宝宝"); | |
echo $baby->doSomething(); | |
echo $baby->bathe(); | |
// 在抽象类中也可以写构造函数 | |
<?php | |
abstract class Animal{ | |
public function __construct($name){ | |
$this->name = $name; | |
} | |
abstract public function doSomething(); | |
abstract public function bathe(); | |
} | |
class Baby extends Animal{ | |
public function bathe(){ | |
return $this->name . "在洗澡" . PHP_EOL; | |
} | |
public function doSomething(){ | |
return $this->name . "在玩耍" . PHP_EOL; | |
} | |
} | |
$baby = new Baby("宝宝"); | |
echo $baby->doSomething(); | |
echo $baby->bathe(); |
# 接口抽象类区别
接口(Interface)和抽象类(Abstract Class)是面向对象编程中常用的两种机制,它们有着一些相似之处,但也有着明显的区别:
相似之处:
- 都可以包含方法的声明:接口和抽象类都可以包含方法的声明,但在接口中,这些方法都是抽象的,而在抽象类中,可以包含抽象方法和非抽象方法。
区别:
- 实现方式:
- 接口:接口中的方法都是抽象的,没有方法体,只包含方法的签名。类通过实现接口来承诺实现接口中声明的所有方法。
- 抽象类:抽象类可以包含抽象方法和非抽象方法。抽象方法只有声明而没有具体实现,而非抽象方法可以有具体的实现。子类继承抽象类后,必须实现所有抽象方法,但可以选择性地覆盖非抽象方法。
- 单继承 vs 多实现:
- 抽象类:PHP 中的类是单继承的,一个类只能继承自一个抽象类(或具体类),但可以实现多个接口。
- 接口:接口支持多继承,一个类可以实现多个接口,从而具备多种类型的行为。
- 目的和使用:
- 接口:主要用于定义类之间的契约,提供了一种多态性的实现方式,可以让不相关的类实现相同的行为。
- 抽象类:主要用于封装类的公共行为,提供了一种代码重用的机制,可以在其中定义一些通用的方法,并在子类中进行扩展和实现。
- 成员变量:
- 接口:不能包含成员变量,只能包含方法的声明。
- 抽象类:可以包含成员变量,也可以包含方法的声明和具体实现。
# 关键字 trait 代码复用
解决单一继承问题
可同时使用多个 trait 用到 use 关键字
trait 中不可有类常量,不可被实例化
<?php | |
trait a{ | |
public function a(){ | |
echo "方法A"; | |
} | |
} | |
trait b{ | |
public function b(){ | |
echo "方法B"; | |
} | |
} | |
class c{ | |
use a,b; | |
} | |
$c = new c(); | |
$c -> a(); | |
$c -> b(); | |
// 打印出方法 A,方法 B | |
//trait 同名方法重命名 | |
<?php | |
trait a{ | |
public function ad() | |
{ | |
echo "方法A"; | |
} | |
} | |
trait b{ | |
public function ad() | |
{ | |
echo "方法B"; | |
} | |
} | |
class c | |
{ | |
use a,b{ | |
// 使用 trait a 中的方法 ad; | |
a::ad insteadof b; | |
// 重命名方法 ad 为 ad2; | |
b::ad as ad2; | |
} | |
} | |
$c = new c(); | |
$c->ad(); | |
$c->ad2(); |