深入理解面向对象,花费时间最后一次整理,以后只更新里面的内容

前言
我相信很多同行去另一家公司之后都会吐槽原来的代码写的太菜。这其实很大一部分原因都来自公司要求“小步快跑,快速迭代”。不同的人重复造轮子,代码质量参差不齐,各种胶水式的代码遍布SVN和Git。大家不敢动原来的代码,只能在最后的地方进行修修补补,所以导致维护困难。

一、面向对象的概念
对象:对象是一些变量和方法都聚集在一起的实体
类:
属性:一个属性是类中的一个内部变量。但从本质上讲,属性是指在类本身,而不是在这个类中任何方法声明的变量;
引用:两个不同的变量名字指向相同的内容;

特点:
1、封装、通过修饰符改变属性或者函数的访问权限,达到保护的作用。

权限修饰符
private: 只能在自己的本类里才能被访问到;
protected: 本类和子类能访问;
public: 本类和子类,还有类外部都可以访问到;

2、继承:子类继承父类的属性和方法,再进一步拓展自己的属性和方法;

1、子类继承父类,子类有父类所有的属性和方法,在子类里面能够操作父类中不是private修饰的属性或者方法;
2、子类的方法会覆盖父类的方法;
3、php不会在子类的构造方法中自动调用父类的构造方法。要执行父类的构造方法,需要在子类的构造方法中调用parent::__construct();
parent:__destruct();

3、多态:通过继承复用实现的;同一个操作作用于不同的类的实例,将产生不同的执行结果。或者不同类的对象收到相同的消息时,将得到不同的结果。

<?php
class animal{
    function can(){
        echo "this function weill be re-write in the children";
    }
}
class cat extends animal{
    function can(){
        echo "I can climb";
    }
}
class dog extends animal{
    function can(){
        echo "I can swim";
    }
}
function test($obj){
    $obj->can();
}
test(new cat());
test(new dog()); 同一个操作作用于不同类的实例,结果也不相同
?>

二、构造方法和析构方法
构造函数:__construct() 主要用来在创建对象时初始化对象,即为对象成员变量赋值,在创建对象的语句中与new运算符一起使用;
析构函数:__destruct() 在某个对象的所有引用都被删除或者当对象被显示销毁时执行;

如何销毁对象
1、unset(),直接赋值为null或者其他值;
2、执行完最后一行代码时进行自动销毁;

$a = new People();
$a = $b = $c;
unset($a);
//此时unset($a)只是$a=null不再指向此对象,但其他变量$b,$c仍然指向此对象,所以该对象不能销毁;

析构函数即使在使用exit()终止脚本运行时也会被调用

php8对构造函数的属性有了进一步的提升:

简单的数值对象:正常的流程:定义变量,构造函数参数定义,给参数赋值;

class Person{
	public string $name;
	public int $age;
	public function __construct($name = '',$age = 0){
		$this->name = $name;
		$this->age = $age;
	}
}

提升后的:一次性完成了成员变量定义,参数定义以及赋值的三个过程

class Person{
	public function __construct(private string name = '',public int $age = 0 ){
		
	}
}

放在构造器提升参数里的属性会同时复制为属性和参数

三、防止重写和被扩展 final
1、如果父类中的方法被声明为final,则不允许被子类方法覆盖。如果一个类被声明为final,则不能被继承;

理解:final意思为最终的,那就是不能对她进行任何操作。

四、接口
1、接口可以指定某个类必须实现哪些方法,但不需要定义这些方法的具体内容;
2、接口通过interface关键字来定义,但其中定义的所有的方法都是空的
3、接口中定义的方法必须是公有的
4、实现一个接口,使用implements操作符
5、类中必须实现接口中定义的所有方法
6、类可以实现多个接口,用逗号来分隔多个接口的名称

<?php
interface Fruit
{

    const MAX_WEIGHT = 5;   //此处不用声明,就是一个静态常量
    function setName($name);
    function getName();
}
//实现接口
class Apple implements Fruit
{

    private $name;
    function getName() {
        return $this->name;
    }
    function setName($_name) {
        $this->name = $_name;
    }
}

$apple = new Apple(); //创建对象
$apple->setName("苹果");
echo "创建了一个" . $apple->getName();
echo "<br />";
echo "MAX_GRADE is " . Apple::MAX_WEIGHT;   //静态常量

//实现多个接口
Class Template implements iTemplate,yTemplate{
	private $age = 19;
	public function setName(){
		return $this->age;
	}
	public function getName($){
		return 'age is '."$age";
	}
}

五、抽象类
1、任何一个类,里面至少有一个方法是被声明为抽象的,则这个类就必须被声明为抽象类;
2、定义为抽象的类不能被实例化;必须先继承抽象来,再实例化子类;
3、被定义为抽象的方法只是声明了其调动的方式,不能定义其具体的功能实现;
4、继承一个抽象类的类,子类必须定义父类中的所有抽象方法;并且这些方法的访问控制必须和父类一样或者更为宽松;比如某个抽象方法被声明为受保护的,那么子类中实现的方法就必须为受保护的或者公有的,不能定义为私有的;

abstract class AbstractClass{
    // 定义抽象方法
    abstract protected function getValue();
    // 普通方法
    public function printOut(){
        print $this->getValue()."";
    }
}

class ConcreteClass extends AbstractClass{
    protected function getValue(){
        return "abstract ";//抽象方法的实现
    }
}
$class1 = new ConcreteClass;
$class1->printOut();

六、traits代码复用
1、php一直是单继承,无法同时从两个基类中继承属性和方法
2、trait不能实例化,通过在类中使用use关键字声明要组合的trait类名;

trait Driver{
	public $carName = "DMW";
	public function driving(){
		echo "driving  {$this->carName}";
	}
}
 
class Person{
	public function age(){
		echo "I am 18 years old";
	}
}

class Student extends Person(){
	use Driver;
	public function study(){
		echo "Learn to drive ";
	}
}
$student = new Student();
$student->study();
$student->age();
$student->driving();

结果:
Learn to drive;
i am 18 years old;
driving BMW;

3、当方法或者属性同名时,当前类中的方法会覆盖trait的方法,而trait的方法又覆盖了基类的方法;
4、组合多个trait,可用都好分割,use trait1,trait2;
5、insteadof是使用某个方法替代另一个,而as是给方法取一个别名;

<?php
trait Trait1 {
    public function hello() {
        echo "Trait1::hello \n";
    }
    public function hi() {
        echo "Trait1::hi \n";
    }
}
trait Trait2 {
    public function hello() {
        echo "Trait2::hello\n";
    }
    public function hi() {
        echo "Trait2::hi\n";
    }
}
class Class1 {
    use Trait1, Trait2 {
        Trait2::hello insteadof Trait1;
        Trait1::hi insteadof Trait2;
    }
}
class Class2 {
    use Trait1, Trait2 {
        Trait2::hello insteadof Trait1;
        Trait1::hi insteadof Trait2;
        Trait2::hi as hei;
        Trait1::hello as hehe;
    }
}
$Obj1 = new Class1();
$Obj1->hello();
$Obj1->hi();
echo "\n";
$Obj2 = new Class2();
$Obj2->hello();
$Obj2->hi();
$Obj2->hei();
$Obj2->hehe();

结果:
Trait2::hello
Trait1::hi


Trait2::hello
Trait1::hi
Trait2::hi
Trait1::hello
<?php
trait Hello {
    public function hello() {
        echo "hello,我是周伯通\n";
    }
}
class Class1 {
    use Hello {
        hello as protected;
    }
}
class Class2 {
    use Hello {
        Hello::hello as private hi;
    }
}
$Obj1 = new Class1();
$Obj1->hello(); # 报致命错误,因为hello方法被修改成受保护的
$Obj2 = new Class2();
$Obj2->hello(); # 输出: hello,我是周伯通,因为原来的hello方法仍然是公共的
$Obj2->hi();  # 报致命错误,因为别名hi方法被修改成私有的

一篇比较好的文章有助于理解traits

七、静态方法和属性
1、声明类属性或方法为static,就可以不实例化类而直接访问;
2、无论函数调用多少次,只初始化一次;

八、魔术方法
定义:某些情况下,会自动调用的方法,称为魔术方法;所有的魔术方法,必须声明为public

魔术方法的参数都不能通过引用传递。
魔术方法不能被声明为static

__clone:克隆方法,当对象被克隆时,将会自动调用;

class Human{
    var $age = 22;
    public function __clone(){
        echo "有人克隆我,假冒";
    }
}
$a = new Human();
$b = clone $a;
echo $b;
结果:有人克隆我,假冒

拓展:深拷贝和浅拷贝
深拷贝:赋值时值被完全赋值,完全的copy,对其中一个做出改变,不会影响另一个
浅拷贝:赋值时,引用赋值。对其中的一个修改,会影响另一个
=赋值时,普通对象是深拷贝,但对对象来说,是浅拷贝。

$m =1;
$n = $m ;此时$n1
$n = 2;
echo $m ;只是值进行赋值,深拷贝 ,值为1

_______________________________
class Test{
	public $a = 1;
}
$m = new Test();
$n = $m;引用赋值
$m->a = 2;
echo $n->a ;结果为2.浅拷贝

__get();当我们调用一个权限上不允许调用的属性或者不存的属性时,会直接走__get()魔术方法取值;

class Human{
    var $age = 22;
    protected $name = "duanjiaqi";
    private $sex = "1";
    public function __get($p){
        echo '您是想访问我的'.$p.'属性';
    }
}
$a = new Human();
echo $a->age.PHP_EOL;//此时age的权限是可以访问的,所以过为22;
echo $a->name.PHP_EOL;//如果没有__get()魔术方法的话,在编辑器里name直接报错,因为属性权限为受保护的,类外部是访问不到的;有__get()魔术方法的话,结果为:您是想访问我的name属性
echo $a->sex.PHP_EOL;
echo $a->no;//类内部没有no属性,没有__get()魔术方法的话直接报错;在__get()魔术方法存在的话,结果为:您是想访问我的no属性

__set():当为无权操作的属性或者不存在的属性赋值时,自动调用__set()魔术方法;

class Human{
    var $age = 22;
    protected $name = "duanjiaqi";
    private $sex = "1";
    public function __set($a,$b){
        echo "设置的".$a."属性,";
        echo "值为".$b.".";
    }
}
$a = new Human();
$a->sex = 33;//sex属性为private,类外部访问不到,所以直接走__set()方法,为sex赋值;结果为:设置的sex属性,值为33.

__isset():当对不可访问或者不存在的属性调用isset()或empty()时,isset()或被调用;

__unset():当对不可访问或者不存在的属性调用unset()时,__unset()会被调用

class Human{
    private $age = 22;
    public function __isset($name)
    {
        // TODO: Implement __isset() method.
        echo 'unset';
    }
}
$a = new Human();
isset($a->age);//如果$age的属性为private或者protected时,则结果为unset,如果$age的属性是public时,则结果为空;

__call():调用不可见或者不存在或者无权限的方法时,自动调用;

__callStatic(): 在静态上下文中调用一个不可访问的方法时,__callStatic()会被调用。

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
?>

__sleep()和__wakeup() serialize()与unserialize()
1、serialize()函数会检查类中是否存在魔术方法__sleep(),如果存在,则该方法会先被调用,然后才执行序列化操作;unserialize()会检查是否存在一个__wakeup()方法,如果存在,则会先调用__wakeup方法,预先准备对象需要的资源。
2、__sleep()常用语提交未提交的数据

__toString():方法用于一个类被当成字符串时应怎样回应

class TestClass{
	public $foo;
	public function __construct($foo){
		$this->foo = $foo;
	}
	public function __toString(){
		return $this->foo;
	}
}
$class = new TestClass('Hello');
echo $class;

结果为:Hello;
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值