慕课网 《PHP面向对象编程》 笔记

114 阅读13分钟

主要内容

  • 面向对象的基本概念
  • PHP中的面向对象的实践

    一、面向对象的基本概念

  • 什么是对象
  • 对象的基本组成
  • 对象的特点
  • 什么是面向对象
  • 为什么使用面向对象
  • 面向对象的实质
  • 面向对象的基本思路
  • 面向对象的基本原则
1.什么是对象
  • 世间万物皆对象 一个人、一辆车、一架飞机、一个桌子、一个键盘 一切可见之物都是对象 一切不可见之物(抽象事物)也可以是对象(一个抽象概念
  • 对象的例子 --NBA球员 詹姆斯 一个人(NBA球员) -- 跑步和跳跃能力非常重要。打篮球时涉及到运球、投篮、扣篮、传球等多项技能。NBA是美国职业篮球的简称,NBA球员都会归属于某个球队打球。
2.对象的基本组成
  • 对象包含两部分 --对象的组成元素 是对象的数据模型,用于描述对象的数据(桌子的颜色、长宽高等数据) 又被称为对象的属性,或者对象的成员变量 --对象的行为 是对象的行为模型,用于描述对象能够做什么事情 又被称为对象的方法
3.对象的特点
  • 每个对象都是独一无二的
  • 对象是一个特定事物,他的职能是完成特定功能
  • 对象是可以重复使用的(可复用)
4.什么是面向对象
  • 什么是面向对象? --面向对象就是在编程的时候一直把对象放在心上
  • 面向对象编程就是在编程的时候数据结构(数据组织方式)都通过对象的结构进行存储 --属性、方法
5.为什么使用面向对象
  • 对象的描述方式更加贴合真实的世界,有利于大型业务的理解
  • 在程序设计的过程中用对象的视角分析世界的时候能够拉近程序设计和真实世界的距离
6.面向对象的实质
  • 面向对象就是把生活中要解决的问题都用对象的方式进行存储 --属性 --方法
  • 对象与对象之间通过方法的调用玩成互动(NBA球员之间传球、运球、投篮) --方法
7.面向对象的基本思路
  • 第一步:识别对象 --任何实体都可以被识别为一个对象
  • 第二步:识别对象的属性 --对象里面存储的数据被识别为属性 --对于不同的业务逻辑,关注的数据不同,对象里面存储的属性也不同
  • 第三步:识别对象的行为 --对象自己属性数据的改变 --对象和外部交互
8.面向对象的基本原则
  • 对象内部是高内聚的 --对象只负责一项特定的职能(职能可大可小) --所有对象相关的内容都封装到对象内部
  • 对象对外是低耦合的 --外部世界可以看到对象的一些属性(并非全部) --外部世界可以看到对象可以做某些事情(并非全部) ####二、PHP中的面向对象的实践
  • 1.基本实践
  • 2.高级实践
  • 3.特殊实践
PHP面向对象的基本实践
  • 类的概念
  • 类的实例化的概念
  • 类的定义和实例化
  • 构造函数
  • 析构函数
  • 对象引用的基本概念
1.类的概念
  • --物以类聚,把具有相似特性的对象归类到一个类中 --类定义了这些相似对象拥有的相同的属性和方法 --类是是相似对象的集合,称为类的定义,是该类对象的蓝图或者原型(属性未被赋予具体数据) --类的对象称为类的一个实例(Instance) --类的属性和方法统称为类成员
  • 例子 --NBA球员就是一个类的定义(Class Definition) --乔丹、詹姆斯、科比称为类的实例(Instancw) image.png image.png
2.类的实例化概念

类的实例化就是通过类定义创建一个类的对象

比如通过NBA球员的定义创建一个球员对象

类的定义里面的属性值都是空的,而对象的属性都有具体的值。

![image.png](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/2de09a83d1f7455bb58e6eabddc50357~tplv-k3u1fbpfcp-jj-mark:0:0:0:0:q75.image#?w=929\&h=532\&s=27678\&e=webp\&b=fdf0ec)
3.类的定义和实例化
<?php
//类的定义以关键词class开始,后面跟着这个类的名称。
//类的命名通常每个单词的第一个字母大写。以中括号开始和结束
class NbaPlayer{
      public $name='Jordan'; //定义属性
      public $height='198cm'; 
      public $weight='98kg'; 
      public $team='Bull';
      public $playerNumber='23'; 

      //定义方法
      public function dribble(){
          echo "Dribbling\n";
      }
      public function pass(){
          echo "Passing\n";
      }
}
//类到对象的实例化
//类的实例化为对象时使用关键字new,new之后紧跟类的名称和一对括号
$jordan = new NbaPlayer();
//对象中的成员属性可以通过->符号来访问
echo $jordan -> name."\n";
//对象中的成员方法可以通过->符号来访问
$jordan -> dribble();
$jordan -> pass();

image.png

4.构造函数
<?php
//类的定义以关键词class开始,后面跟着这个类的名称。
//类的命名通常每个单词的第一个字母大写。以中括号开始和结束
class NbaPlayer{
      public $name='Jordan'; //定义属性
      public $height='198cm'; 
      public $weight='98kg'; 
      public $team='Bull';
      public $playerNumber='23'; 
      //构造函数,在对象被实例化的时候自动调用
      function  __construct($name,$height,$weight,$team,$playerNumber){
              echo "In NbaPlayer constructor\n";
              $this->name = $name;//$this是php里面的伪变量,表示对象自身。可以通过$this->的方式访问对象的属性和方法
              $this->height = $height;
              $this->weight = $weight;
              $this->team = $team;
              $this->playerNumber = $playerNumber;
      }

      //定义方法
      public function dribble(){
          echo "Dribbling\n";
      }
      public function pass(){
          echo "Passing\n";
      }
}
//类到对象的实例化
//类的实例化为对象时使用关键字new,new之后紧跟类的名称和一对括号
$jordan = new NbaPlayer("Jordan2","198cm","98kg","Bull","23");
//对象中的成员属性可以通过->符号来访问
echo $jordan -> name."\n";
//对象中的成员方法可以通过->符号来访问
$jordan -> dribble();
$jordan -> pass();
//每一次用new实例化对象时,都会用实例化后的类名后面的参数列表调用构造函数
$james = new NbaPlayer("James","203cm","120kg","Heat","6");
echo $james->name."\n";

image.png

5.析构函数
<?php
//类的定义以关键词class开始,后面跟着这个类的名称。
//类的命名通常每个单词的第一个字母大写。以中括号开始和结束
class NbaPlayer{
      public $name='Jordan-origin'; //定义属性
      public $height='198cm'; 
      public $weight='98kg'; 
      public $team='Bull';
      public $playerNumber='23'; 
      //构造函数,在对象被实例化的时候自动调用
      function  __construct($name,$height,$weight,$team,$playerNumber){
              echo "In NbaPlayer constructor\n";
              $this->name = $name;//$this是php里面的伪变量,表示对象自身。可以通过$this->的方式访问对象的属性和方法
              $this->height = $height;
              $this->weight = $weight;
              $this->team = $team;
              $this->playerNumber = $playerNumber;
      }
	  //析构函数,在程序执行结束的时候会自动调用
	  //析构函数通常被用于清理程序使用的资源。比如程序使用了打印机,那么可以在析构函数里面释放打印机资源。
	  function __destruct(){
		  echo "Destroying ".$this->name."\n";
	  }

      //定义方法
      public function dribble(){
          echo "Dribbling\n";
      }
      public function pass(){
          echo "Passing\n";
      }
}
//类到对象的实例化
//类的实例化为对象时使用关键字new,new之后紧跟类的名称和一对括号
$jordan = new NbaPlayer("Jordan","198cm","98kg","Bull","23");
//对象中的成员属性可以通过->符号来访问
echo $jordan -> name."\n";
//对象中的成员方法可以通过->符号来访问
$jordan -> dribble();
$jordan -> pass();
//每一次用new实例化对象时,都会用实例化后的类名后面的参数列表调用构造函数
$james = new NbaPlayer("James","203cm","120kg","Heat","6");
echo $james->name."\n";
//通过实例化变量设置为null,可以触发析构函数的调用
$james = null;
echo "From now on James will not be used.\n";

image.png

6.对象引用的基本概念
<?php
//类的定义以关键词class开始,后面跟着这个类的名称。
//类的命名通常每个单词的第一个字母大写。以中括号开始和结束
class NbaPlayer{
      public $name='Jordan-origin'; //定义属性
      public $height='198cm'; 
      public $weight='98kg'; 
      public $team='Bull';
      public $playerNumber='23'; 
      //构造函数,在对象被实例化的时候自动调用
      function  __construct($name,$height,$weight,$team,$playerNumber){
              echo "In NbaPlayer constructor\n";
              $this->name = $name;//$this是php里面的伪变量,表示对象自身。可以通过$this->的方式访问对象的属性和方法
              $this->height = $height;
              $this->weight = $weight;
              $this->team = $team;
              $this->playerNumber = $playerNumber;
      }
	  //析构函数,在程序执行结束的时候会自动调用
	  //析构函数通常被用于清理程序使用的资源。比如程序使用了打印机,那么可以在析构函数里面释放打印机资源。
	  function __destruct(){
		  echo "Destroying ".$this->name."\n";
	  }

      //定义方法
      public function dribble(){
          echo "Dribbling\n";
      }
      public function pass(){
          echo "Passing\n";
      }
}
//类到对象的实例化
//类的实例化为对象时使用关键字new,new之后紧跟类的名称和一对括号
$jordan = new NbaPlayer("Jordan","198cm","98kg","Bull","23");
//对象中的成员属性可以通过->符号来访问
echo $jordan -> name."\n";
//对象中的成员方法可以通过->符号来访问
$jordan -> dribble();
$jordan -> pass();
//每一次用new实例化对象时,都会用实例化后的类名后面的参数列表调用构造函数
$james = new NbaPlayer("James","203cm","120kg","Heat","6");
echo $james->name."\n";
//通过实例化变量设置为null,可以触发析构函数的调用
//当对象不会再被使用的时候,会触发析构函数
$james1 = $james;
$james2 = &$james;
$james = null;
$james1 = null;
echo "From now on James will not be used.\n";

image.png

PHP面向对象的高级实践
  • 继承
  • 访问控制
  • Static(静态)关键字
  • 重写
  • Final关键字
  • 数据访问补充
  • 接口
  • 多态
  • 抽象类
1.继承
  • 继承的好处

父类里面定义的类成员可以不用在子类中重复定义,节约了编程的时间和代价

比如,人的吃这个方法一旦在父类中定义,那么NBA球员和女主播两个子类就不需要实现吃这个方法了,就好像天生就有这个功能一样

同一个父类的子类拥有相同的父类定义的类成员,因此外部代码调用它们的时候可以一视同仁

比如,一个NBA球员和一个女主播,因为他们都是人,所以可以直接调用父类定义的“吃”方法,而不用管他到底是个NBA球员还是个女主播

子类可以修改和调整父类定义的类成员 我们称为重写(Overwrite)一旦子类修改了,就按照子类修改之后的功能执行

  • 案例
<?php
//类的定义以关键词class开始,后面跟着这个类的名称。
//类的命名通常每个单词的第一个字母大写。以中括号开始和结束
class Human{
	  public $name; //定义属性
      public $height; 
      public $weight; 
	  
	  public function eat($food){
		  echo $this->name." is eating ".$food."\n";
	  }
	
}


//在PHP中可以用extens关键字来表示类的继承,后面跟父类的类名。
//PHP中extends后面只能跟一个类的类名,这就是php的单继承原则
class NbaPlayer extends Human{
   
      public $team='Bull';
      public $playerNumber='23'; 
      //构造函数,在对象被实例化的时候自动调用
      function  __construct($name,$height,$weight,$team,$playerNumber){
              echo "In NbaPlayer constructor\n";
              $this->name = $name;//$this是php里面的伪变量,表示对象自身。可以通过$this->的方式访问对象的属性和方法
              $this->height = $height; //父类中的属性可以通过$this来访问
              $this->weight = $weight;
              $this->team = $team;
              $this->playerNumber = $playerNumber;
      }
	  //析构函数,在程序执行结束的时候会自动调用
	  //析构函数通常被用于清理程序使用的资源。比如程序使用了打印机,那么可以在析构函数里面释放打印机资源。
	  function __destruct(){
		  echo "Destroying ".$this->name."\n";
	  }

      //定义方法
      public function dribble(){
          echo "Dribbling\n";
      }
      public function pass(){
          echo "Passing\n";
      }
}
//类到对象的实例化
//类的实例化为对象时使用关键字new,new之后紧跟类的名称和一对括号
$jordan = new NbaPlayer("Jordan","198cm","98kg","Bull","23");
//对象中的成员属性可以通过->符号来访问
echo $jordan -> name."\n";
$jordan->eat("Apple");//在子类的对象上可以直接访问父类中定义的方法和属性

2.访问控制
  • 问题

--NBA球员一般都不想让其他人知道自己的真实年龄怎么办?

  • 面向对象的三种访问权限

--public 公有的类成员,可以在任何地方被访问

定义该成员的类(自身)、该类的子类、其他类

--protected 受保护的类成员,可以被其自身以及其子类访问

--private 私有的类成员,只能被自身访问

<?php
//类的定义以关键词class开始,后面跟着这个类的名称。
//类的命名通常每个单词的第一个字母大写。以中括号开始和结束
class Human{
	  public $name; //定义属性
      protected $height; //只有自身和子类可以访问
      public $weight; 
	  private $isHungry= true;//不能被子类访问
	  
	  public function eat($food){
		  echo $this->name." is eating ".$food."\n";
	  }
	  public function info(){
		  echo "HUMAN: ".$this->name.";".$this->height.";".$this->isHungry."\n";
	  }
}


//在PHP中可以用extens关键字来表示类的继承,后面跟父类的类名。
//PHP中extends后面只能跟一个类的类名,这就是php的单继承原则
class NbaPlayer extends Human{
   
      public $team='Bull';
      public $playerNumber='23'; 
	  private $age='40'; //Private的类成员只能在类的内部被访问
      //构造函数,在对象被实例化的时候自动调用
      function  __construct($name,$height,$weight,$team,$playerNumber){
              echo "In NbaPlayer constructor\n";
              $this->name = $name;//$this是php里面的伪变量,表示对象自身。可以通过$this->的方式访问对象的属性和方法
              $this->height = $height; //父类中的属性可以通过$this来访问
              $this->weight = $weight;
              $this->team = $team;
              $this->playerNumber = $playerNumber;
			//  echo $this->isHungry."\n"; //访问不了父类的Private私有类成员
      }
	  //析构函数,在程序执行结束的时候会自动调用
	  //析构函数通常被用于清理程序使用的资源。比如程序使用了打印机,那么可以在析构函数里面释放打印机资源。
	  function __destruct(){
		  echo "Destroying ".$this->name."\n";
	  }

      //定义方法
      public function dribble(){
          echo "Dribbling\n";
      }
      public function pass(){
          echo "Passing\n";
      }
	  
	  public function getAge(){
		  echo $this->name."'s age is ".($this->age - 2)."\n"; //对private类属性进行假封装
	  }
}
//类到对象的实例化
//类的实例化为对象时使用关键字new,new之后紧跟类的名称和一对括号
$jordan = new NbaPlayer("Jordan","198cm","98kg","Bull","23");
//echo $jordan->height."\n"; //类的外部访问不了Protected的受保护类成员
$jordan->info();

image.png

3.静态成员Static
  • 问题

--所有NBA球员都有一个共同的联盟总裁“David Stern”

--总裁换成了“Adam Silver” 怎么办?

换是一个动词,所以应该对应类里面的一个方法

  • 使用类的静态成员特性就可以达到这样的效果

--static关键字

--静态成员Static的使用占用内存,可快速读取,一般用作为不经常变动、公用的类成员上

--静态成员不用new 进行对象实例化

--静态成员不再通过$this访问对象的属性和方法而是通过self::

  • 使用静态成员的场景(作为公共类成员)
<?php
//类的定义以关键词class开始,后面跟着这个类的名称。
//类的命名通常每个单词的第一个字母大写。以中括号开始和结束
class Human{
	  public $name; //定义属性
      protected $height; //只有自身和子类可以访问
      public $weight; 
	  private $isHungry= true;//不能被子类访问
	  public static $sValue = "Static value in HumanClass";
	  public function eat($food){
		  echo $this->name." is eating ".$food."\n";
	  }
	  public function info(){
		  echo "HUMAN: ".$this->name.";".$this->height.";".$this->isHungry."\n";
	  }

	  }


//在PHP中可以用extens关键字来表示类的继承,后面跟父类的类名。
//PHP中extends后面只能跟一个类的类名,这就是php的单继承原则
class NbaPlayer extends Human{
   
      public $team='Bull';
      public $playerNumber='23'; 
	  private $age='40'; //Private的类成员只能在类的内部被访问
	  //静态属性定义时在访问控制关键词后面添加static关键字即可
	  public static $presidentName="David";
	  
	  //静态方法定义
	  public static function changePresident($newPresident){
		  //在类定义中使用静态成员的时候,用self或者static关键字后面跟着::操作符,即可。注意,
		  //在访问静态成员的时候,::后面需要跟$符号
			self::$presidentName = $newPresident;
			//static::$presidentName = $newPresident;
		  //使用parent关键字就能够访问父类的静态成员
			echo parent::$sValue."\n";
	  }
	  
      //构造函数,在对象被实例化的时候自动调用
      function  __construct($name,$height,$weight,$team,$playerNumber){
              echo "In NbaPlayer constructor\n";
              $this->name = $name;//$this是php里面的伪变量,表示对象自身。可以通过$this->的方式访问对象的属性和方法
              $this->height = $height; //父类中的属性可以通过$this来访问
              $this->weight = $weight;
              $this->team = $team;
              $this->playerNumber = $playerNumber;
			//  echo $this->isHungry."\n"; //访问不了父类的Private私有类成员
      }
	  //析构函数,在程序执行结束的时候会自动调用
	  //析构函数通常被用于清理程序使用的资源。比如程序使用了打印机,那么可以在析构函数里面释放打印机资源。
	  function __destruct(){
		  echo "Destroying ".$this->name."\n";
	  }

      //定义方法
      public function dribble(){
          echo "Dribbling\n";
      }
      public function pass(){
          echo "Passing\n";
      }
	  
	  public function getAge(){
		  echo $this->name."'s age is ".($this->age - 2)."\n"; //对private类属性进行假封装
	  }
}
//类到对象的实例化
//类的实例化为对象时使用关键字new,new之后紧跟类的名称和一对括号
$jordan = new NbaPlayer("Jordan","198cm","98kg","Bull","23");
$james = new NbaPlayer("James","203cm","120kg","Heat","6");
//在类定义外部访问静态属性,我们可以用类名加::操作符的方法来访问类的静态成员。
echo NbaPlayer::$presidentName." before change\n";
NbaPlayer::changePresident("Joy");
echo NbaPlayer::$presidentName."\n";
echo Human::$sValue."\n"; 

4.重写与Final关键字
  • 问题

    --对于吃饭这个功能,不希望子类修改它

    --对于NBA球员这个类,不希望再创建它的子类

  • 使用类的Final成员特性就可以达到这样的效果

    --Final关键字(PHP5)

<?php
//子类中编写跟父类完全一致的方法可以完成对父类方法的重写(overwrite)
//对于不想被任何类继承的类可以在class之前添加final关键字
//对于不想被子类重写(修改)的方法,可以在方法定义的前面添加final关键字
final class BaseClass{
	public function test(){
		echo "BaseClass::test called\n";
	}
	//添加final关键字能够让这个方法不能在子类中重写
	final public function test1(){
		echo "BaseClass::test1 called\n";
	}
	
}
class ChildClass extends BaseClass{
	public function test($tmp=null){
		echo "ChildClass::test called".$tmp."\n";
	}
	public function test1(){
		echo "ChildClass::test1 called\n";
	}
	
}

$obj = new ChildClass();
// $obj->test("tmp")."\n";
$obj->test1()."\n";
5.数据访问补充
<?php
//1.parent关键字可以用于调用父类中被子类重写了的方法
//2.self关键字可以用于访问类自身的成员方法,也可以用于访问自身的静态成员和类常量;
//不能用于访问类自身的属性;使用常量的时候不需要在常量名称前面添加$符号
//3.static关键字用于访问类自身定义的静态成员,访问静态属性时需要在属性前面添加$符号
class BaseClass{
	public function test(){
		echo "BaseClass::test called\n";
	}
	//添加final关键字能够让这个方法不能在子类中重写
	final public function test1(){
		echo "BaseClass::test1 called\n";
	}
	
}
class ChildClass extends BaseClass{
	const CONST_VALUE='A constant value';
	private static $sValue = 'static value';
	public function test($tmp=null){
		echo "ChildClass::test called".$tmp."\n";
		parent::test();//用parent关键字可以访问父类中被子类重写的方法
		//访问called方法可以通过$this或者self::
		self::called();//能访问自身类的方法,不能访问类自身的属性
		echo self::CONST_VALUE."\n";
		echo static::$sValue."\n";//或者echo self::$sValue."\n";
	}
	public function called(){
		echo "ChildClass::called() called\n";
		
	}
}

$obj = new ChildClass();
$obj->test()."\n";

image.png

6.接口
  • 问题

    --人会吃饭,动物也会吃饭

    --有些植物也可以吃东西(比如猪笼草)

  • 吃这个动作(方法)动物和植物的实现方式是不一样的

    --吃这个行为没有统一的实现方式

    --动物张嘴咀嚼

    --植物直接包裹融化

  • 接口就是把不同类的共同行为进行定义,然后在不同的类里面实现不同的功能

    image.png

<?php
//interface关键字用于定义接口
interface ICanEat{
	//接口里面的方法不需要有方法的实现
	public function eat($food);
}
//implements关键字用于表示类实现某个接口
class Hunman implements ICanEat{
	//实现了某个接口之后,必须提供接口中定义的方法的具体实现。
	public function eat($food){
		echo "Hunman eating ".$food."\n";
	}
}

class Animal implements ICanEat{
	//实现了某个接口之后,必须提供接口中定义的方法的具体实现。
	public function eat($food){
		echo "Animal eating ".$food."\n";
	}
}

$obj = new Hunman();
$obj->eat("Apple");
$monkey = new Animal();
$monkey->eat("Banana");

//不能实例化接口
//$eatObj = new ICanEat();

//可以用instanceof关键字来判断某个对象是否实现了某个接口
var_dump($obj instanceof ICanEat);


function checkEat($obj){
	if($obj instanceof ICanEat){
		$obj->eat('food');
	}else{
		echo "The obj can't eat.\n";
	}
}

checkEat($obj);
checkEat($monkey);

//接口继承 可以用extends让接口继承接口
interface ICanPee extends ICanEat{
	public function pee();
}
//当类实现子接口时,父接口定义的方法也需要在这个类里面具体实现
class Hunman1 implements ICanPee{
	public function pee(){}
	public function eat($food){}
}
	
  • 某个类实现(implements)了某个接口和继承(extends)了某个类的区别

    --实现接口跟继承类很类似,但是接口不能直接创建自己的对象

    如果创建了“会吃东西”这个接口的对象,那么具体怎么吃根本不知道

    --继承的父类必须有该方法的具体实现,子类可以重写父类的方法,也可以不重写

    --接口里面的方法是不需要具体实现的,只要定义了方法的名称和参数就可以了,具体的实现必须在实现类中定义

    --一句话概括:类的方法必须有实现,接口的方法必须为空

    image.png

7.多态
  • 因为接口的方法实现可以有很多,所以对于接口里面定义的方法的具体实现是多种多样的,这种特性我们称为--多态

    --比如接口A有两个实现B和C,B和C对A里面定义的方法的实现可以是不同的,这种现象就是多态

<?php
//interface关键字用于定义接口
interface ICanEat{
	//接口里面的方法不需要有方法的实现
	public function eat($food);
}
//implements关键字用于表示类实现某个接口
class Hunman implements ICanEat{
	//实现了某个接口之后,必须提供接口中定义的方法的具体实现。
	public function eat($food){
		echo "Hunman eating ".$food."\n";
	}
}

class Animal implements ICanEat{
	//实现了某个接口之后,必须提供接口中定义的方法的具体实现。
	public function eat($food){
		echo "Animal eating ".$food."\n";
	}
}

$obj = new Hunman();
$obj->eat("Apple");
$monkey = new Animal();
$monkey->eat("Banana");

//不能实例化接口
//$eatObj = new ICanEat();

//可以用instanceof关键字来判断某个对象是否实现了某个接口
var_dump($obj instanceof ICanEat);


function checkEat($obj){
	if($obj instanceof ICanEat){
		$obj->eat('food');
	}else{
		echo "The obj can't eat.\n";
	}
}
//相同的一行代码,对于传入不同的接口的实现的对象的时候,表现是不同的,这就是多态
checkEat($obj);
checkEat($monkey);

//接口继承 可以用extends让接口继承接口
interface ICanPee extends ICanEat{
	public function pee();
}
//当类实现子接口时,父接口定义的方法也需要在这个类里面具体实现
class Hunman1 implements ICanPee{
	public function pee(){}
	public function eat($food){}
}

image.png

7.抽象类
  • 接口里面的方法都是没有实现的,而类里面的方法都是有实现的。

  • 有没有一种形态,允许类里面一部分方法不实现呢?

    --当接口中的某些方法对于所有的实现类都是一样的实现方法,只有部分方法需要用到多态的特性

  • 实例

    --人和动物吃东西是不同的,但是呼吸是相同的,不需要为人和动物分别实现呼吸的功能

<?php
//abstract关键字用于定义抽象类
abstract class AcanEat{
	//在抽象方法面前添加Abstract关键字可以标明这个方法是抽象方法不要具体的实现
	abstract public function eat($food);
	//抽象类中可以包含普通的方法,有方法的具体实现
	public function breath(){
		echo "Breath use the air. \n";
	}
	
}
//继承抽象类的关键字是extends
class Human extends AcanEat{
	//继承抽象类的子类需要实现抽象类中定义的抽象方法
	public function eat($food){
		echo "Human eating ".$food."\n";
	}
	
}

class Animal extends AcanEat{
	public function eat($food){
		echo "Animal eating ".$food."\n";
	}
	
}

$man  = new Human();
$man->eat("Apple");
$man->breath();
$monkey=new Animal();
$monkey->eat("Banana");
$monkey->breath();

image.png

PHP面向对象的特殊实践(魔术方法)
  • 魔术方法之__tostring和__invoke方法
  • 魔术方法之__call和__callStatic方法
  • 魔术方法之__get和__set方法
  • 魔术方法之__isset和__unset方法
  • 魔术方法之__clone方法
1.魔术方法之__tostring和__invoke方法
  • __tostring --当对象呗当做String使用时,这个方法会被自动调用 --Echo $obj;
  • __invoke --当对象被当成方法调用时,这个方法会被自动调用 --$obj(4);
<?php
class MagicTest{
	public function __tostring(){
		return "This is the Class MagicTest \n";
	}
	public function __invoke($x){
		echo  "__invoke called with parameter ".$x."\n";
	}
}

$obj = new MagicTest();
echo $obj;
$obj(4);

image.png

2.魔术方法之__call和__callStatic方法
  • __call --当对象访问不存在的方法名称时,__call()方法会被自动调用
  • __callStatic --当对象访问不存在的静态方法名称时,__callStatic()方法会被自动调用
  • 这两个方法在PHP里面也被称为方法的重载(overloading) --注意区分重写 --通过这个两个方法,同一个方法的名称的调用可以对应不同的方法实现
<?php
class MagicTest{
	public function __tostring(){
		return "This is the Class MagicTest \n";
	}
	public function __invoke($x){
		echo  "__invoke called with parameter ".$x."\n";
	}
	//方法的重载
	//这个方法的参数第一个就是调用的方法的名称,第二个参数是方法调用的参数组成的数组
	public function __call($name,$arguments){
		echo "Calling ".$name." with parameters: ".implode(",",$arguments)."\n";
		
	}
	//静态方法的重载,注意这个方法需要设定为static
	public static function __callStatic($name,$arguments){
		echo "Static calling ".$name." with parameters: ".implode(",",$arguments)."\n";
		
	}
}

$obj = new MagicTest();
//通过__call跟__callStatic魔术方法, 同样的方法名称可以响应正常调用和非正常调用
$obj->runTest("para1","para2");
MagicTest::runTest("para1","para2");

image.png

3.魔术方法之__get和__set与__isset和__unset方法
  • __get()、__set()、__isset()、__unset()

    --在给不可访问属性赋值,__set()会被调用。

    --读取不可访问属性的值时,__get()会被调用。

    --当对不可访问属性调用isset()或empty()时,__isset()会被调用。

    --当对不可访问属性调用unset时,__unset()会被调用。

    --所谓不可访问属性,实际上就是在调用某个属性时发现这个属性没有被定义,这时候不同的操作会触发不用的魔术方法

    --这几个方法也被称为属性重载的魔术方法

<?php
class MagicTest{
	public function __tostring(){
		return "This is the Class MagicTest \n";
	}
	public function __invoke($x){
		echo  "__invoke called with parameter ".$x."\n";
	}
	//方法的重载
	//这个方法的参数第一个就是调用的方法的名称,第二个参数是方法调用的参数组成的数组
	public function __call($name,$arguments){
		echo "Calling ".$name." with parameters: ".implode(",",$arguments)."\n";
		
	}
	//静态方法的重载,注意这个方法需要设定为static
	public static function __callStatic($name,$arguments){
		echo "Static calling ".$name." with parameters: ".implode(",",$arguments)."\n";
		
	}
	
	public function __get($name){
		return "Getting the property ".$name."\n";
		
	}
	public function __set($name,$value){
		echo  "Getting the property ".$name." to value ".$value."\n";
		
	}
	public function __isset($name){
		echo  "__isset invoked \n";
		return false;
		
	}
	public function __unset($name){
		echo  "unsetting property ".$name."\n";
		
	}
}

$obj = new MagicTest();
echo $obj->className;
$obj->className = 'MagicClassX';
echo '$obj->className is set?'.isset($obj->className)."\n";
echo '$obj->className is empty?'.empty($obj->className)."\n";
unset($obj->className);

image.png

4.魔术方法之__clone方法

image.png __clone方法在调用clone方法的时候进行数据的预处理或初始化

<?php
class NbaPlayer{
	public $name;
	function __clone(){
		$this->name = 'TBD';
	}
}
$james = new NbaPlayer();
$james->name = 'James';
echo $james->name."\n";
$james2 = clone $james;
echo "Before set up: James2's:".$james2->name."\n";
$james2->name = 'James2';
echo "James's:".$james->name."\n";
echo "James2's".$james2->name."\n";

image.png