当前位置: 首页 > news >正文

猿创征文|学习记录之 PHP 中的面向对象编程

本文目录

      • 一、什么是面向对象编程
      • 二、类和对象
        • (一)定义类
        • (二)实例化类(创建对象)
        • (三)类中的变量
        • (四)类中的方法
          • 1. 定义方法
          • 2. 调用方法
          • 3. 方法的参数
          • 4. 方法参数的默认值
          • 5. 方法的返回值
      • 三、$this 操作符的使用
      • 四、构造方法
      • 五、析构方法
      • 六、类的继承
        • (一)如何继承类
        • (二)方法的重写
        • (三)调用父类中的方法
      • 七、静态变量(方法)
      • 八、final 类和方法
      • 九、常量属性
      • 十、abstract 类和方法
      • 十一、接口
      • 十二、魔术方法
        • (一)__set() 方法
        • (二)__get() 方法
        • (三)__call() 方法
        • (四)__toString() 方法

一、什么是面向对象编程

面向对象,准确地说应该叫做 “面向对象编程”。

面向对象编程( Object Oriented Programming,OOP )是一种计算机编程架构,它能使代码更加简洁,更易于维护,并且具有更强的可重用性。

二、类和对象

类( class )和对象( object )是面向对象编程的核心概念。

类是对一类事物的描述,它定义了事物的抽象特点,类的定义包含了数据的形式以及对数据的操作。

对象是类的实例,是实际存在的该类事物的某个个体。

在计算机中,可以理解为类是一个抽象模型,而对象是实实在在存储在内存区域中的一个实体。

(一)定义类

PHP 也是通过关键字 class 加类名来声明类的,与一个类关联的代码必须用大括号括起来。

其定义的格式如下:

<?php

class Test
{
	
}

类名可以是任意数字和字母的组合,但不能以数字开头,一般采用首字母大写,而后每个单词首字母大写的形式,以便于阅读。

(二)实例化类(创建对象)

<?php

class Test
{

}
// 实例化类
$test = new Test();

(三)类中的变量

类中的变量,是指在 class 中声明的变量,也称成员变量(也称为属性),用于存放数据信息。

成员变量与普通变量相似,其定义的格式如下:

key $age = "23";

关键字 key 可以是 publicprotectedprivatestaticfinal 中的任意一个。

public(公有):表示变量在类的内部和外部都可以被读取和修改。
protected(受保护):表示变量可以被其自身以及其子类和父类读取和修改。
private(私有):表示变量只能被其定义所在的类访问。

要访问成员变量,可以使用 “->” 符号连接对象和变量名。

<?php

class Test
{
	// 声明成员变量
    public $age = "23";
}

// 实例化类
$test = new Test();
// 读取成员变量 $age 的值
echo $test->age; // 23

也可以给成员变量赋值(public 修饰的):

<?php

class Test
{
    public $age = "23";
}

// 实例化类
$test = new Test();
$test->age = 25;
echo $test->age; // 25

(四)类中的方法

类中的方法(又叫成员方法)是指在类中声明的特殊函数。

它与普通函数的区别在于,普通函数实现的是某个独立的功能;而成员方法是实现类的一个行为,是类的一部分。

1. 定义方法
<?php

class Test
{
    public $age = "23";
    
    // 定义 say 方法
    public function say()
    {
        echo '这是say方法';
    }
}
2. 调用方法
<?php

class Test
{
    public $age = "23";

    public function say()
    {
        echo '这是say方法';
    }
}

// 实例化类
$test = new Test();
// 调用 say 方法
$test->say(); // 这是say方法
3. 方法的参数
<?php

class Test
{
    public function say($name)
    {
        echo 'name的值为' . $name;
    }
}

$test = new Test();
$test->say('tom'); // name的值为tom

如果声明类的方法时带有参数,而调用该方法时没有传递参数,或者参数数量不够,系统将会报错。如果参数数量超过方法本身定义参数的数量,PHP会忽略后面多出来的参数,不会报错。

4. 方法参数的默认值
<?php

class Test
{
    public function say($name = '默认值')
    {
        echo 'name的值为' . $name;
    }
}

$test = new Test();
// 调用时不传参
$test->say(); // name的值为默认值
5. 方法的返回值
<?php

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 '<br />';
        // 使用 $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 来声明:

<?php

// 定义父类 Test
class 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 继承父类 Test
class 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),也称为方法的重写。

在对父类的方法进行重写时,子类中的方法必须与父类中对应的方法具有相同的名称。

<?php

class Test
{
    protected function say()
    {
        echo '我是父类中的 say 方法。';
    }
}

// 定义子类 Test2 继承父类 Test
class Test2 extends Test
{
	// 此处的权限可以是 protected 或 public ,但不能是 private 。
	// 可以加参数。
    public function say($param = '')
    {
        echo '我是子类中的 say 方法。';
        echo '父类中的 say 方法不满足我的要求,我被重写。';
    }
}

$test2 = new Test2();
$test2->say();
// 我子父类中的 say 方法。父类中的 say 方法不满足我的要求,我被重写。

在重写方法时需注意以下几点:

  1. 子类中的覆盖方法不能使用比父类中被覆盖方法更严格的访问权限。在声明方法时如果没有定义访问权限,则权限默认为 public
  2. 子类中的覆盖方法可以拥有与父类中被覆盖方法不同的参数数量。
  3. 父类中的构造方法也可以被重写。

(三)调用父类中的方法

即使父类中的方法被重写,但父类中的方法仍保留其功能,可以使用 Parent 关键字调用父类中的方法。

<?php

class Test
{
    protected function say()
    {
        echo '我是父类中的 say 方法。';
    }
}

// 定义子类 Test2 继承父类 Test
class Test2 extends Test
{
    public function say()
    {
        // 调用父类中的 say 方法
        Parent::say();
        echo '我子父类中的 say 方法。';
    }
}

$test2 = new Test2();
$test2->say();
// 我是父类中的 say 方法。我子父类中的 say 方法。

七、静态变量(方法)

前面的内容中,类被当做模板,对象被当做活动组件,面向对象编程中的操作都是通过类的实例(对象)来完成的。

事实上,并不是所有的变量(方法)都要通过创建对象来调用。

声明类属性或方法为 static(静态),就可以不实例化类而直接访问。

使用静态成员,除了不需要实例化对象外还有一个好处,就是在对象被销毁后,依然保存被修改的静态数据,以便下次继续使用。

<?php

class Test
{
    public static $n = 1;

    public static function test1()
    {
        // 在类内使用 self 关键字访问静态成员变量
        echo self::$n;
    }
}

// 类外访问静态成员变量 $n
echo Test::$n; // 1
// 类外访问静态成员方法 test1
Test::test1(); // 1
// 仍然可以实例化后调用静态方法
$t = new Test();
$t->test1();  // 1

静态属性不能通过一个类已实例化的对象来访问,但静态方法可以。由于静态方法不需要通过对象即可调用,所以伪变量 $this 在静态方法中不可用。静态属性不可以由对象通过 -> 操作符来访问。

<?php

class Test
{
    public static $n = 1;

    public static function test1()
    {
        echo '值为:' . self::$n;
        self::$n++;
        echo '<br />';
    }
}

Test::test1(); // 值为:1
Test::test1(); // 值为:2

可以发现两次返回的结果是有联系的。

八、final 类和方法

继承为类的应用带来了巨大的灵活性。

通过覆写类和方法,调用同样的成员方法可以得到完全不同的结果,但有时候,也需要类或方法保持不变,这就需要用到 final 关键字。

<?php

// 使用 final 修饰类
final class Test {}

class Test2 extends Test {}

报错:Class Test2 may not inherit from final class (Test)

<?php

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()

总结:

  1. final 关键字修饰的类不能被继承。
  2. final 关键字修饰的成员方法不能被重写。

当不希望一个类被继承时,可以将该类声明为 final 类型;当不希望类中的某个方法被子类重写时,可以设置其为 final 类型的方法。

九、常量属性

可以把在类中始终保持不变的值定义为常量。

PHP 中使用 const 关键字定义常量,在定义和使用常量时不需要使用 $ 符号。

另外使用 const 定义的常量名称一般都大写。

类中常量的使用方法类似于静态变量,所不同的是它的值不能被改变。

<?php

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 关键字修饰的类或方法,称为抽象类或者抽象方法。

抽象类不能被直接实例化,只能作为其他类的父类来使用。

抽象方法只是声明了其调用方式(参数),不能定义其具体的功能实现。

子类可以继承它并通过实现其中的抽象方法,来使抽象类具体化。

任何一个类,如果它里面至少有一个方法是被声明为抽象的,那么该类就必须被声明为抽象的。

抽象类可以像普通类那样去声明,但必须以分号而不是方法体结束。

<?php

// 使用 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,不可以为 privateprotected

<?php

// 声明接口 1
interface Test1
{
    public function say1();
}

// 声明接口 2
interface 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() 方法包含两个参数,分别表示变量名称和变量值,两个参数均不可省略。

<?php

class Test
{
    // 定义 __set() 方法
    public function __set($name, $value)
    {
        echo '正在试图给一个未定义的成员变量赋值,变量名称:' . $name . ',变量值:' . $value . '。';
    }
}

$t = new Test();
$t->name = 'test'; // 正在试图给一个未定义的成员变量赋值,变量名称:name,变量值:test。

(二)__get() 方法

当需要调用一个未定义或不可见(私有)的成员变量时,可以使用 __get() 方法读取变量值。

__get() 方法有一个参数,表示要调用的变量名。

<?php

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() 方法来存储方法名及其参数。

该方法包含 “方法名” 和 “方法参数” 两个参数,其中的 “方法参数” 以数组形式存在。

<?php

class Test
{
    // 定义 _call() 方法
    public function __call($name, $params)
    {
        echo '正在试图调用一个未定义的成员方法,方法名称:' . $name . '。';
        echo '<br />';
        echo '传递的参数有:';
        print_r($params);
    }
}

$t = new Test();
echo $t->say('a', 'b');
// 正在试图调用一个未定义的成员方法,方法名称:say。
// 传递的参数有:Array ( [0] => a [1] => b )

(四)__toString() 方法

__toString() 方法用于在使用 echoprint 输出对象时,将对象转化为字符串。

<?php

class Test
{
    // 定义 __toString() 方法
    public function __toString()
    {
        return 'string';
        // 这里必须返回一个字符串
    }
}

$t = new Test();
echo $t;

相关文章:

  • 猿创征文|UDP/TCP网络编程
  • 1-丁基-3-甲基咪唑双三氟甲基磺酰亚胺([BMIm] NTf2)离子液体修饰Ni镍纳米颗粒的介绍
  • 定时任务报警通知解决方案详解
  • 【js】js实现分页02
  • “结构体名”和“结构体名是个指针”的区别
  • 网课查题api接口使用方法
  • 干货分享|优炫数据库支撑GIS融合的探索
  • 重写DATAGRID控件,当对行编辑后重LOAD后可定位到最后点击的那一行。
  • 基于强化学习的空域作战辅助决策(1D)
  • 【AGC】使用云调试优惠扣费、华为设备上触发崩溃、无法下载华为应用市场问题小结
  • elasticsearch设置密码
  • 大学生制作自己的查题搜题公众号 简单方便
  • 【postgresql 数据库运维文档】
  • 搜题公众号 大学生搭建查题公众号到底有多简单
  • js split , slice, splice 三者区别
  • CODING 缺陷管理功能正式开始公测
  • ERLANG 网工修炼笔记 ---- UDP
  • GitUp, 你不可错过的秀外慧中的git工具
  • HTTP中GET与POST的区别 99%的错误认识
  • Java-详解HashMap
  • PHP 使用 Swoole - TaskWorker 实现异步操作 Mysql
  • php的插入排序,通过双层for循环
  • Redis在Web项目中的应用与实践
  • Sass 快速入门教程
  • Spark学习笔记之相关记录
  • Travix是如何部署应用程序到Kubernetes上的
  • 从tcpdump抓包看TCP/IP协议
  • 分享一份非常强势的Android面试题
  • 基于Vue2全家桶的移动端AppDEMO实现
  • 前端每日实战:70# 视频演示如何用纯 CSS 创作一只徘徊的果冻怪兽
  • 前嗅ForeSpider教程:创建模板
  • 如何将自己的网站分享到QQ空间,微信,微博等等
  • 设计模式走一遍---观察者模式
  • 使用Envoy 作Sidecar Proxy的微服务模式-4.Prometheus的指标收集
  • 以太坊客户端Geth命令参数详解
  • 由插件封装引出的一丢丢思考
  • 远离DoS攻击 Windows Server 2016发布DNS政策
  • ​HTTP与HTTPS:网络通信的安全卫士
  • ​力扣解法汇总946-验证栈序列
  • #、%和$符号在OGNL表达式中经常出现
  • #我与Java虚拟机的故事#连载03:面试过的百度,滴滴,快手都问了这些问题
  • (+3)1.3敏捷宣言与敏捷过程的特点
  • (超简单)构建高可用网络应用:使用Nginx进行负载均衡与健康检查
  • (二)fiber的基本认识
  • (附源码)spring boot车辆管理系统 毕业设计 031034
  • (附源码)spring boot儿童教育管理系统 毕业设计 281442
  • (附源码)springboot人体健康检测微信小程序 毕业设计 012142
  • (附源码)SSM环卫人员管理平台 计算机毕设36412
  • (附源码)ssm基于微信小程序的疫苗管理系统 毕业设计 092354
  • (过滤器)Filter和(监听器)listener
  • (接口自动化)Python3操作MySQL数据库
  • (每日持续更新)jdk api之FileFilter基础、应用、实战
  • (十二)python网络爬虫(理论+实战)——实战:使用BeautfulSoup解析baidu热搜新闻数据
  • (四)模仿学习-完成后台管理页面查询
  • (算法)Game