猿创征文|学习记录之 PHP 中的面向对象编程
本文目录
一、什么是面向对象编程
面向对象,准确地说应该叫做 “面向对象编程”。
面向对象编程( Object Oriented Programming,OOP
)是一种计算机编程架构,它能使代码更加简洁,更易于维护,并且具有更强的可重用性。
二、类和对象
类( class
)和对象( object
)是面向对象编程的核心概念。
类是对一类事物的描述,它定义了事物的抽象特点,类的定义包含了数据的形式以及对数据的操作。
对象是类的实例,是实际存在的该类事物的某个个体。
在计算机中,可以理解为类是一个抽象模型,而对象是实实在在存储在内存区域中的一个实体。
(一)定义类
PHP
也是通过关键字 class
加类名来声明类的,与一个类关联的代码必须用大括号括起来。
其定义的格式如下:
class Test{}
类名可以是任意数字和字母的组合,但不能以数字开头,一般采用首字母大写,而后每个单词首字母大写的形式,以便于阅读。
(二)实例化类(创建对象)
class Test{}// 实例化类$test = new Test();
(三)类中的变量
类中的变量,是指在 class
中声明的变量,也称成员变量(也称为属性),用于存放数据信息。
成员变量与普通变量相似,其定义的格式如下:
key $age = "23";
关键字 key
可以是 public
,protected
,private
,static
和 final
中的任意一个。
public
(公有):表示变量在类的内部和外部都可以被读取和修改。
protected
(受保护):表示变量可以被其自身以及其子类和父类读取和修改。
private
(私有):表示变量只能被其定义所在的类访问。
要访问成员变量,可以使用 “->” 符号连接对象和变量名。
class Test{// 声明成员变量 public $age = "23";}// 实例化类$test = new Test();// 读取成员变量 $age 的值echo $test->age; // 23
也可以给成员变量赋值(public
修饰的):
class Test{ public $age = "23";}// 实例化类$test = new Test();$test->age = 25;echo $test->age; // 25
(四)类中的方法
类中的方法(又叫成员方法)是指在类中声明的特殊函数。
它与普通函数的区别在于,普通函数实现的是某个独立的功能;而成员方法是实现类的一个行为,是类的一部分。
1. 定义方法
class Test{ public $age = "23"; // 定义 say 方法 public function say() { echo '这是say方法'; }}
2. 调用方法
class Test{ public $age = "23"; public function say() { echo '这是say方法'; }}// 实例化类$test = new Test();// 调用 say 方法$test->say(); // 这是say方法
3. 方法的参数
class Test{ public function say($name) { echo 'name的值为' . $name; }}$test = new Test();$test->say('tom'); // name的值为tom
如果声明类的方法时带有参数,而调用该方法时没有传递参数,或者参数数量不够,系统将会报错。如果参数数量超过方法本身定义参数的数量,PHP会忽略后面多出来的参数,不会报错。
4. 方法参数的默认值
class Test{ public function say($name = '默认值') { echo 'name的值为' . $name; }}$test = new Test();// 调用时不传参$test->say(); // name的值为默认值
5. 方法的返回值
class Test{ public function say($name = '默认值') { // 使用 return 关键字返回值 return 'name的值为' . $name; }}$test = new Test();// 使用 echo 将返回值输出echo $test->say('jack'); // name的值为jack
三、$this 操作符的使用
如果想在类内调用本类中的成员变量或成员方法,就要使用伪变量 $this->
。$this
就是指本身,所以 $this->
只能在类的内部使用。
class Test{ public $name = '小明'; public $age = 23; public function say() { // 使用 $this 访问本类中的 $name 变量 echo '我的名字是:' . $this->name; echo '
'; // 使用 $this 访问本类中的 sayAge 方法 $this->sayAge(); } public function sayAge() { echo '我的年龄是:' . $this->age; }}$test = new Test();$test->say(); // 我的名字是:小明 我的年龄是:23
四、构造方法
构造方法是一种特殊的方法,主要用于在创建对象时初始化对象,即为对象成员变量赋初始值,总与 new
运算符一起使用在创建对象的语句中。
class Test{// 定义成员变量 public $name; public $age; public $sex;// 定义构造方法 public function __construct($name, $age, $sex) { // 为成员变量赋值 $this->name = $name; $this->age = $age; $this->sex = $sex; }}// 实例化类并给构造方法传值$test = new Test('小明', '25', '男');echo $test->name;echo $test->age;echo $test->sex;// 输出:小明 25 男
构造方法会在实例化类时自动执行。
注意:方法开始的 “__” 是两条下划线 “_” 。
五、析构方法
析构方法(析构函数)与构造方法正好相反,当对象结束其生命周期时(例如对象所在的函数已调用完毕),系统自动执行析构函数以释放内存。
class Test{ public function __destruct() { echo '我是析构方法'; }}$test = new Test();
PHP 使用 “垃圾回收” 机制,自动清除不再使用的对象,释放内存。就是说即便不使用 unset 函数,系统也会自动调用析构方法,此处只是说明析构方法在何时会被调用。一般情况下不用手动创建析构方法。另外,当对象没有被引用时也同样会被销毁。
六、类的继承
类可以从其他类中扩展出来,扩展或派生出来的类拥有其基类(父类)的所有变量和函数,并包含所有派生类(子类)中定义的新功能,这称为继承。
继承是面向对象最重要的特点之一,可以实现对类的复用。
(一)如何继承类
PHP
是单继承的,一个扩充类只能继承一个基类,但一个父类却可以被多个子类所继承。
子类不能继承父类的私有属性和私有方法。
在 PHP 5
之后的版本中,类的方法可以被继承,类的构造函数也能被继承。
当子类被实例化时,PHP
会先在子类中查找构造方法,如果子类有自己的构造方法,PHP
会优先调用子类中的构造方法;当子类中没有时,PHP
会转而去调用父类中的构造方法。
继承使用关键字 extends
来声明:
// 定义父类 Testclass Test{ public $name = '小明'; protected $age = 22; private $sex = '男';public function __construct() { echo '我是父类中的构造方法,当继承我的子类中没有构造方法时,我就执行。'; }public function sayName(){echo $this->name;}protected function sayAge(){echo $this->age;}private function saySex(){echo $this->sex;}}// 定义子类 Test2 继承父类 Testclass Test2 extends Test{ public function __construct() { echo '我是子类中的构造方法,我会优先执行'; }}// 实例化子类$test2 = new Test2();echo $test2->name; // 输出:小明echo $test2->age;// 报错:Cannot access protected property Test2::$age// protected 修饰的成员变量不能在类外使用echo $test2->sex; // 无内容,private 修饰的成员变量不会被继承$test2->sayName(); // 输出:小明$test2->sayAge();// 报错:Call to protected method Test::sayAge() from context// protected 修饰的成员方法不能在类外使用$test2->saySex(); // 无内容,private 修饰的成员方法不会被继承$test2->sayAge2(); // 输出:22// 在子类的内部,可以访问 protected 修饰的成员变量
总结权限修饰符的修饰范围:
(二)方法的重写
如果从父类继承的方法不能满足子类的需求,可以对其进行改写,该过程叫做方法的覆盖(override
),也称为方法的重写。
在对父类的方法进行重写时,子类中的方法必须与父类中对应的方法具有相同的名称。
class Test{ protected function say() { echo '我是父类中的 say 方法。'; }}// 定义子类 Test2 继承父类 Testclass Test2 extends Test{// 此处的权限可以是 protected 或 public ,但不能是 private 。// 可以加参数。 public function say($param = '') { echo '我是子类中的 say 方法。'; echo '父类中的 say 方法不满足我的要求,我被重写。'; }}$test2 = new Test2();$test2->say();// 我子父类中的 say 方法。父类中的 say 方法不满足我的要求,我被重写。
在重写方法时需注意以下几点:
- 子类中的覆盖方法不能使用比父类中被覆盖方法更严格的访问权限。在声明方法时如果没有定义访问权限,则权限默认为
public
。 - 子类中的覆盖方法可以拥有与父类中被覆盖方法不同的参数数量。
- 父类中的构造方法也可以被重写。
(三)调用父类中的方法
即使父类中的方法被重写,但父类中的方法仍保留其功能,可以使用 Parent
关键字调用父类中的方法。
class Test{ protected function say() { echo '我是父类中的 say 方法。'; }}// 定义子类 Test2 继承父类 Testclass Test2 extends Test{ public function say() { // 调用父类中的 say 方法 Parent::say(); echo '我子父类中的 say 方法。'; }}$test2 = new Test2();$test2->say();// 我是父类中的 say 方法。我子父类中的 say 方法。
七、静态变量(方法)
前面的内容中,类被当做模板,对象被当做活动组件,面向对象编程中的操作都是通过类的实例(对象)来完成的。
事实上,并不是所有的变量(方法)都要通过创建对象来调用。
声明类属性或方法为 static
(静态),就可以不实例化类而直接访问。
使用静态成员,除了不需要实例化对象外还有一个好处,就是在对象被销毁后,依然保存被修改的静态数据,以便下次继续使用。
class Test{ public static $n = 1; public static function test1() { // 在类内使用 self 关键字访问静态成员变量 echo self::$n; }}// 类外访问静态成员变量 $necho Test::$n; // 1// 类外访问静态成员方法 test1Test::test1(); // 1// 仍然可以实例化后调用静态方法$t = new Test();$t->test1(); // 1
静态属性不能通过一个类已实例化的对象来访问,但静态方法可以。由于静态方法不需要通过对象即可调用,所以伪变量 $this 在静态方法中不可用。静态属性不可以由对象通过 -> 操作符来访问。
class Test{ public static $n = 1; public static function test1() { echo '值为:' . self::$n; self::$n++; echo '
'; }}Test::test1(); // 值为:1Test::test1(); // 值为:2
可以发现两次返回的结果是有联系的。
八、final 类和方法
继承为类的应用带来了巨大的灵活性。
通过覆写类和方法,调用同样的成员方法可以得到完全不同的结果,但有时候,也需要类或方法保持不变,这就需要用到 final
关键字。
// 使用 final 修饰类final class Test {}class Test2 extends Test {}
报错:Class Test2 may not inherit from final class (Test)
。
class Test{ // 使用 final 修饰成员方法 final public function say() { echo 'function say'; }}class Test2 extends Test{ // 子类重写父类 say 方法 public function say() { echo 'function say'; }}
报错:Cannot override final method Test::say()
。
总结:
- 被
final
关键字修饰的类不能被继承。 - 被
final
关键字修饰的成员方法不能被重写。
当不希望一个类被继承时,可以将该类声明为 final
类型;当不希望类中的某个方法被子类重写时,可以设置其为 final
类型的方法。
九、常量属性
可以把在类中始终保持不变的值定义为常量。
PHP
中使用 const
关键字定义常量,在定义和使用常量时不需要使用 $
符号。
另外使用 const
定义的常量名称一般都大写。
类中常量的使用方法类似于静态变量,所不同的是它的值不能被改变。
class Test{// 定义常量 const VERSION = '1.0'; public function getVersion() { // 类内访问常量 echo self::VERSION; }}// 类外访问常量echo Test::VERSION; // 1.0$t = new Test();$t->getVersion(); // 1.0
常量的值必须是一个定值,不能是变量、类属性、数学运算的结果或函数调用。
十、abstract 类和方法
使用 abstract
关键字修饰的类或方法,称为抽象类或者抽象方法。
抽象类不能被直接实例化,只能作为其他类的父类来使用。
抽象方法只是声明了其调用方式(参数),不能定义其具体的功能实现。
子类可以继承它并通过实现其中的抽象方法,来使抽象类具体化。
任何一个类,如果它里面至少有一个方法是被声明为抽象的,那么该类就必须被声明为抽象的。
抽象类可以像普通类那样去声明,但必须以分号而不是方法体结束。
// 使用 abstract 声明抽象类abstract class Test{ // 抽象方法只有方法的声明部分,没有方法体。 public abstract function say($name);}// 抽象类不能被实例化。// $t = new Test();// 报错:Cannot instantiate abstract class Test 。// 抽象类只能作为其它类的父类来使用class Test2 extends Test{ // 继承一个抽象类的时候,父类中的所有抽象方法在子类中必须被重写。 // 这些方法的访问控制必须和父类中一样(或者更为宽松)。 public function say($name) { echo 'function say echo:' . $name; }}$t = new Test2();$t->say('hello'); // function say echo:hello
抽象方法只有方法的声明部分,没有方法体。
继承一个抽象类的时候,父类中的所有抽象方法在子类中必须被重写,这些方法的访问控制必须和父类中一样(或者更为宽松)。
例如某个抽象方法被声明为受保护的,那么子类中实现的方法就应该声明为受保护的或者公有的,而不能定义为私有的。
方法的调用方式必须匹配,即类型和所需参数数量必须一致。
十一、接口
PHP
只支持单继承,父类可以派生出多个子类,但一个子类只能继承自一个父类。
接口有效地解决了这一问题。
接口是一种类似于类的结构,使用它可以指定某个类必须实现哪些方法。
它只包含方法原型,不需要包含方法体。
这些方法原型必须被声明为 public
,不可以为 private
或 protected
。
// 声明接口 1interface Test1{ public function say1();}// 声明接口 2interface Test2{ public function say2();}// 实现接口class Test implements Test1, Test2{ public function say1() { echo '实现接口1中的say1方法'; } public function say2() { echo '实现接口2中的say2方法'; }}$t = new Test();$t->say1(); // 实现接口1中的say1方法$t->say2(); // 实现接口2中的say2方法
实现接口的类中必须实现接口中定义的所有方法,除非该类被声明为抽象类。
十二、魔术方法
在 PHP
中以两个下划线 “__” 开头的方法被称为“魔术方法”,是系统预定义的方法。
如果需要使用这些魔术方法,必须先在类中定义。
构造方法 “__construct()” 和析构方法 “__destruct()” 都属于魔术方法。
魔术方法的作用、方法名、使用的参数列表和返回值都是规定好的,在使用这些方法时,需要用户自己根据需求编写方法体的内容。
使用时无须调用,它会在特定情况下自动被调用。
PHP 将所有以 __(两个下划线)开头的类方法保留为魔术方法。所以在定义类方法时,除魔术方法外,建议不要以 __ 为前缀。
(一)__set() 方法
在 PHP
程序试图给一个未定义的属性赋值时,就会调用 __set()
方法。
__set()
方法包含两个参数,分别表示变量名称和变量值,两个参数均不可省略。
class Test{ // 定义 __set() 方法 public function __set($name, $value) { echo '正在试图给一个未定义的成员变量赋值,变量名称:' . $name . ',变量值:' . $value . '。'; }}$t = new Test();$t->name = 'test'; // 正在试图给一个未定义的成员变量赋值,变量名称:name,变量值:test。
(二)__get() 方法
当需要调用一个未定义或不可见(私有)的成员变量时,可以使用 __get()
方法读取变量值。
__get()
方法有一个参数,表示要调用的变量名。
class Test{private $age = 21; // 定义 __get() 方法 public function __get($name) { echo '正在试图获取一个未定义的成员变量的值,变量名称:' . $name . '。'; }}$t = new Test();echo $t->name; // 正在试图获取一个未定义的成员变量的值,变量名称:name。echo $t->age; // 获取私有成员变量也会触发 __get 方法。
(三)__call() 方法
程序试图调用不存在或不可见的成员方法时,PHP会自动调用 __call()
方法来存储方法名及其参数。
该方法包含 “方法名” 和 “方法参数” 两个参数,其中的 “方法参数” 以数组形式存在。
class Test{ // 定义 _call() 方法 public function __call($name, $params) { echo '正在试图调用一个未定义的成员方法,方法名称:' . $name . '。'; echo '
'; echo '传递的参数有:'; print_r($params); }}$t = new Test();echo $t->say('a', 'b');// 正在试图调用一个未定义的成员方法,方法名称:say。// 传递的参数有:Array ( [0] => a [1] => b )
(四)__toString() 方法
__toString()
方法用于在使用 echo
或 print
输出对象时,将对象转化为字符串。
class Test{ // 定义 __toString() 方法 public function __toString() { return 'string'; // 这里必须返回一个字符串 }}$t = new Test();echo $t;
来源地址:https://blog.csdn.net/ZhangJiWei_2019/article/details/126636392
免责声明:
① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。
② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341