OOP基础概念介绍

本文详细解释了面向对象编程的基础概念,包括面向对象的特征(封装、继承、多态和抽象)、抽象类与接口的区别、MVC架构、final关键字、trait的使用、类的自动加载、构造函数和析构函数,以及PHP中的重载和魔术方法。
摘要由CSDN通过智能技术生成


一、 什么是面向对象

面向对象(Object Oriented, 简称OO)是一种软件开发方法和编程范式,它是对现实世界理解和抽象的一种方法。

面向对象的方法将数据(对象)和对这些数据的操作方法组合在一起,形成一个相互依存的整体。在面向对象编程中,对同类抽象出其共性,形成类(Class),每个类封装了数据(属性)和方法(操作),并且通常只允许本类的方法来处理这些数据。类通过简单的外部接口与外界交互,而对象之间通过发送消息来通信。

面向对象的概念和应用已经超出了程序设计和软件开发的范畴,扩展到了数据库系统交互式界面、应用结构、应用平台、分布式系统、网络管理结构、计算机辅助设计(CAD)技术、人工智能等多个领域。它是计算机编程技术发展到一定阶段的产物,提供了一种从更高层次进行系统建模的方式,这种方式更贴近于事物的自然运行模式。

二、 面向对象的特征有哪些

主要有 封装, 继承, 多态。如果是 4 个方面则加上:抽象。

  1. 封装: 封装性就是把一个类(对象)的属性和方法封装起来,外部只有被授权的方法或操作才可以访问
    php提供了三种访问控制符
    public 表示全局的,本类内部,类外部,子类都可以访问 。
    protected 表示受保护的,只有本类或子类可以访问 。
    private 表示私有的,只有本类内部可以访问,外部和子类都不可以访问 。
  2. 继承: 面向对象的继承性是指子类可以继承父类的属性和方法,并且子类可以自定义属性和方法
  3. 多态: 多态性是指同一个对象在不同的情形下(被实例化),表现出不同的形态。
  4. 抽象: 抽象就是找出一些事物的相似和共性之处,然后将这些事物归为一个类,这个类只考虑这些事物的相似和共性之处,并且会忽略与当前主题和目标无关的那些方面,将注意力集中在与当前目标有关的方面。

三、抽象类和接口的概念以及区别

1、抽象类

  • 它是一种特殊的,不能被实例化的类,只能作为其他类的父类使用。使用 abstract 关键字声明。
  • PHP 有抽象类和抽象方法。
  • 定义为抽象的类不能被实例化。任何一个类,如果它里面至少有一个方法是被声明为抽象的,那么这个类就必须被声明为抽象的。
  • 被定义为抽象的方法只是声明了其调用方式(参数),不能定义其具体的功能实现。继承一个抽象类的时候,子类必须定义父类中的所有抽象方法

2、接口

  • 它是一种特殊的抽象类,也是一个特殊的类,使用 interface 声明。
  • 指定某个类必须实现哪些方法,但不需要定义这些方法的具体内容。 接口中定义的所有方法都必须是 public

3、区别:

  • 抽象类的操作通过继承关键字 extends 实现,而接口的使用是通过 implements 关键字来实现。
  • 抽象类中有数据成员,可以实现数据的封装,但是接口没有数据成员。
  • 抽象类中可以有构造方法,但是接口没有构造方法。
  • 抽象类的方法(抽象方法和非抽象方法)可以通过 private、protected、public 关键字修饰(抽象方法不能是 private),而接口中的方法只能使用 public 关键字修饰。
  • 一个类只能继承于一个抽象类,而一个类可以同时实现多个接口。 (PHP实现多继承-- 接口和trait)
  • 抽象类中可以有成员方法的实现代码(非抽象方法可以有方法体),而接口中不可以有成员方法的实现代码。

四、MVC 分别指哪三层,有什么优点

MVC 三层分别指:业务模型、视图、控制器,由控制器层调用模型处理数据,然后将数 据映射到视图层进行显示

  • 优点
    • 可以实现代码的重用性,避免产生代码冗余;
    • M和V 实现代码分离,从而使同一个程序可以使用不同的表现形式

五、 php中final 关键字的理解

PHP 5 新增了一个 final 关键字。如果父类中的方法被声明为 final,则子类无法覆盖该方法。如果一个类被声明为final,则不能被继承

六、PHP中trait的理解

  • trait是php5.4之后出现的一种代码复用的方式
  • trait是为类似PHP的单继承语言而准备的一种代码复用机制.**Trait为了减少单继承语言的限制,**使开发人员能够自由地在不同层次结构内独立的类中复用method
  • Trait和class类似,但无法通过trtait自身来实例化,它为传统继承增加来水平特性的组合,可以同时使用多个trait. 如果多个trait中有方法冲突的话可以引用trait时候设置别名,类似use的别名
<?php
trait Trait_a {
    function getReturnType() { /*1*/ }
    function getReturnDescription() { /*2*/ }
}
trait Trait_b {
    function getReturnType() { /*1*/ }
    function getReturnDescription() { /*2*/ }
}
class class1 extends parent_class1 {
    use Trait_a;
/* ... */
}
class class2 extends parent_class2 {
    use Trait_a,Trait_b;
    /* ... */
}

使用use语句利用这个特性。通过逗号分隔,在 use 声明列出多个 trait,可以都插入到一个类中。

1、继承顺序

从基类继承的成员会被 trait 插入的成员所覆盖。优先顺序是来自当前类的成员覆盖了 trait 的方法,而 trait 则覆盖 了被继承的方法。

也就是 当前类 > trait > 父类

2、冲突的解决

如果两个 trait 都插入了一个同名的方法,如果没有明确解决冲突将会产生一个致命错误。为了解决多个 trait 在同一个类中的命名冲突,需要使用 insteadof 操作符来明确指定使用冲突方法中的哪一个。以上方式仅允许排除掉其它方法,as 操作符可以 为某个方法引入别名。 注意,as 操作符不会对方法进行重命名,也 不会影响其方法。

在本例中 Talker 使用了 trait A 和 B。由于 A 和 B 有冲突的方法,其定义了使用 trait B 中的 smallTalk 以及 trait A中的 bigTalk。 Aliased_Talker 使用了 as 操作符来定义了 talk 来作为 B 的 bigTalk 的别名

<?php
trait A {
    public function smallTalk() {
        echo 'a';
    }
    public function bigTalk() {
				echo 'A'; 
    }
}

trait B {
    public function smallTalk() {
				echo 'b'; 
    }
    public function bigTalk() {
        echo 'B';
		} 
}

class Talker {
    use A, B {
        B::smallTalk insteadof A;
        A::bigTalk insteadof B;
    }
}

class Aliased_Talker {
    use A, B {
      B::smallTalk insteadof A; 
      A::bigTalk insteadof B;
      B::bigTalk as talk;
 }
}
?>

七、类的自动加载

1、自动加载函数 spl_autoload_register

spl_autoload_register() 函数可以注册任意数量的自动加载器,当使用尚未被定义的类(class)和接口(interface)时自动去加载。通过注册自动加载器,脚本引擎在 PHP 出错失败前有了最后一个机会加载所需的类。
注意: PHP 8.0.0 之前,可以使用 __autoload() 自动加载类和接口。 然而,它是 [spl_autoload_register() 的一种不太灵活的替代方法,并且 __autoload()在 PHP 7.2.0 起弃用,在 PHP 8.0.0 起移除。

2、自动加载原理

  1. 使用这个魔术函数的基本条件是类文件的文件名要和类的名字保持一致。
  2. 当程序执行到实例化某个类的时候,如果在实例化前没有引入这个类文件,那么就自动 执行__autoload()函数。
  3. 这个函数会根据实例化的类的名称来查找这个类文件的路径,当判断这个类文件路径下 确实存在这个类文件后 就执行 include 或者 require 来载入该类,然后程序继续执行,如果这个路径下不存在该文件时就提示错误。
  4. 使用自动载入的魔术函数可以不必要写很多个 include 或者 require 函数。

八、构造函数和析构函数

1、构造函数 __construct()

  • __construct([mixed] ...$values = “”): void 具有构造函数的类会在每次创建新对象时先调用此方法,所以非常适合在使用对象之前做一些初始化工作。
  • 如果子类中定义了构造函数则不会隐式调用其父类的构造函数。要执行父类的构造函数,需要在子类的构造函数中调用 parent::__construct()
  • 如果子类没有定义构造函数则会如同一个普通的类方法一样从父类继承

2、析构函数 ​ __destruct()

  • __destruct(): void 析构函数会在到某个对象的所有引用都被删除或者当对象被显式销毁时执行。
  • 析构函数即使在使用 exit() 终止脚本运行时也会被调用。
  • 在析构函数中调用 exit() 将会中止其余关闭操作的运行。

九、PHP重载

PHP所提供的重载(overloading)是指动态地创建类属性和方法。
我们是通过魔术方法(magic methods)来实现的,当调用当前环境下未定义或不可见(不可访问(protected 或 private)的类属性或方法时,重载方法会被调用。

1、属性重载

属性重载只能在对象中进行。在静态方法中,这些魔术方法将不会被调用。所以这些方法都不能被声明为 static。将这些魔术方法定义为 static 会产生一个警告。

  • public __set(string $name, mixed $value): void
    在给不可访问(protected 或 private)或不存在的属性赋值时,__set()] 会被调用。

  • public __get(string $name): mixed
    读取不可访问(protected 或 private)或不存在的属性的值时,__get() 会被调用。

  • public __isset(string $name): bool
    当对不可访问(protected 或 private)或不存在的属性调用isset() 或者empty() 时,__isset() 会被调用。

  • public __unset(string $name): void
    当对不可访问(protected 或 private)或不存在的属性调用 unset() , __unset()会被调用。

2、属性重载示例

<?php
class PropertyTest {
     /**  被重载的数据保存在此  */
    private $data = array();

 
     /**  重载不能被用在已经定义的属性  */
    public $declared = 1;

     /**  只有从类外部访问这个属性时,重载才会发生 */
    private $hidden = 2;

    public function __set($name, $value) 
    {
        echo "Setting '$name' to '$value'\n";
        $this->data[$name] = $value;
    }

    public function __get($name) 
    {
        echo "Getting '$name'\n";
        if (array_key_exists($name, $this->data)) {
            return $this->data[$name];
        }

        $trace = debug_backtrace();
        trigger_error(
            'Undefined property via __get(): ' . $name .
            ' in ' . $trace[0]['file'] .
            ' on line ' . $trace[0]['line'],
            E_USER_NOTICE);
        return null;
    }

    public function __isset($name) 
    {
        echo "Is '$name' set?\n";
        return isset($this->data[$name]);
    }

    public function __unset($name) 
    {
        echo "Unsetting '$name'\n";
        unset($this->data[$name]);
    }

    /**  非魔术方法  */
    public function getHidden() 
    {
        return $this->hidden;
    }
}

echo "<pre>\n";

$obj = new PropertyTest;

$obj->a = 1;
echo $obj->a . "\n\n";

var_dump(isset($obj->a));
unset($obj->a);
var_dump(isset($obj->a));
echo "\n";

echo $obj->declared . "\n\n";

echo "Let's experiment with the private property named 'hidden':\n";
echo "Privates are visible inside the class, so __get() not used...\n";
echo $obj->getHidden() . "\n";
echo "Privates not visible outside of class, so __get() is used...\n";
echo $obj->hidden . "\n";
?>

以上例程会输出:
Setting 'a' to '1'
Getting 'a'
1

Is 'a' set?
bool(true)
Unsetting 'a'
Is 'a' set?
bool(false)

1

Let's experiment with the private property named 'hidden':
Privates are visible inside the class, so __get() not used...
2
Privates not visible outside of class, so __get() is used...
Getting 'hidden'

Notice:  Undefined property via __get(): hidden in <file> on line 70 in <file> on line 29

3、方法重载

  • public __call(string $name, array $arguments): mixed
    在对象中调用一个不可访问方法时,__call() 会被调用。

  • public static __callStatic(string $name, array $arguments): mixed
    在静态上下文中调用一个不可访问方法时,__callStatic()会被调

4、方法重载示例

<?php
class MethodTest 
{
    public function __call($name, $arguments) 
    {
        // 注意: $name 的值区分大小写
        echo "Calling object method '$name' " . implode(', ', $arguments). "\n";
    }

    public static function __callStatic($name, $arguments) 
    {
        // 注意: $name 的值区分大小写
        echo "Calling static method '$name' " . implode(', ', $arguments). "\n";
    }
}

$obj = new MethodTest;
$obj->runTest('in object context');

MethodTest::runTest('in static context');
?>
  
以上例程会输出:
Calling object method 'runTest' in object context
Calling static method 'runTest' in static context  

类的魔术方法

魔术方法 是一种特殊的方法,当对对象执行某些操作时会覆盖 PHP 的默认操作。
PHP 保留所有以 __ 开头的方法名称。 因此,除非覆盖 PHP 的行为,否则不建议使用此类方法名称。
下列方法名被认为是魔术方法:

1. __construct() 实例化类时自动调用  new  classs()
   __destruct() 类对象使用结束时自动调用(unset或者程序执行结束)
2. __set() 在给不可访问|不存在的属性赋值的时候调用。
   __get() 获取不可访问|不存在的属性时候调用。
3. __isset() 当对不可访问|不存在的属性调用 isset()|empty() 会被调用。   
   __unset() 当对不可访问|不存在的属性调用 unset()时候会调用。
4. __sleep(),__serialize() 
   serialize() 函数会检查类中是否存在一个魔术方法 __serialize()|__sleep() 。如果存在,该方法将在任何序列化之前优先执行。它必须以一个代表对象序列化形式的键/值 成对的关联数组形式来返回,如果类中同时定义了 __serialize() 和 __sleep() 两个魔术方法,则只有 __serialize() 方法会被调用。
   
5. __wakeup(),__unserialize()
   unserialize() 检查是否存在具有名为 __unserialize()|__wakeup() 的魔术方法。如果存在,此函数将会传递从 __serialize() 返回的恢复数组。然后它可以根据需要从该数组中恢复对象的属性。如果类中同时定义了__unserialize() 和 __wakeup() 两个魔术方法,则只有 __unserialize() 方法会生效
   
6. __call() 调用一个不存在的方法的时候调用。
   __callStatic()调用一个不存在的静态方法是调用。
7. __toString() 把对象转换成字符串的时候会调用。比如 echo。
8. __invoke() 当尝试把对象当方法调用时调用。
9. __set_state() 当使用 var_export()函数时候调用。接受一个数组参数。
10. __clone() 当使用 clone 复制一个对象时候调用。

总结

以上就是对面向对象OOP的一些理解和总结,如有不妥之处尽管提出一起学习讨论。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

baiyyxx

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值