PHP 面向对象知识点

PHP技术
411
0
0
2023-02-14

定义基本的类: 在类中我们可以定义各种数据成员和成员函数,其中public修饰的函数与变量可以在任何地方被调用,而private修饰的函数只能在本类中被调用子类不可调用,而protected修饰的则可以在本类和子类中被调用但不可以在外部调用.

<?php
	class SportObj
	{
		public $username = "lyshark";         // 公有属性
		private $password = "12345";          // 私有属性
		protected $type = "root";             // 受保护的

		function init($name,$height,$age,$sex)
		{
			echo "当前的类名是: " . get_class($this) . "<br>";
			if($height >= 180 and $age >= 18)
				return "用户: " . $name . " 符合 <br>";
			else
				return "用户: " . $name . " 不符合 <br>";
		}
		
		function GetVariable()
		{
			echo "用户名: {$this->username} 密码: {$this->password} 类型: {$this->type} <br>";
		}
	}

// 实例化
$person = new SportObj();

echo "访问公有属性: {$person->username} <br>";
echo $person->init("lyshark","180","20","男");
echo $person->GetVariable();
?>

构造函数/析构函数: 在PHP中声明构造函数使用__construct,而声明析构函数则使用__destruct,构造函数主要完成对类的初始化工作,析构函数则主要负责对类的清理工作.

<?php
	class Books
	{
		public $obj_name;
		public $obj_type;

		// 定义构造函数
		public function __construct($name,$type){
			$this->obj_name = $name;
			$this->obj_type = $type;
			echo "默认自动执行构造方法" . "<br>";
		}
		// 定义析构函数
		public function __destruct(){
			echo "函数执行完成之后清理工作" . "<br>";
		}

		public function boolObj(){
			if ($this->obj_type >= "10" and $this->obj_name == "lyshark")
				return $this->obj_name;
			else
				return "-1";
		}
	}

$obj = new Books("lyshark","100");
echo $obj->boolObj() . "<br>";
?>

面向对象继承: 子类继承父类的所有成员变量和方法包括构造方法,当子类被创建时PHP会先在子类中查找构造方法,如果子类有自己的构造方法,那么PHP会率先调用子类的方法,当子类没有时,PHP则会调用父类的构造方法,这就是PHP中的继承.

<?php
	# 创建父类,或者说基类
	class BaseObj{
		public $name;
		public $age;
		public $sex;

		public function __construct($name,$age,$sex){
			$this->name = $name;
			$this->age = $age;
			$this->sex = $sex;
		}
		public function showMe(){
			echo "这句话不会显示." . "<br>";
		}
		public function extenShow(){
			echo "hello world";
		}
	}

	# 子类继承父类的所有方法
	class BeatBask extends BaseObj{
		public $height;

		public function __construct($name,$height){
			$this->height = $height;
			$this->name = $name;
		}
		public function showMe(){
			if($this->height >= 100)
			{
				return $this->name . ", 符合条件!";
			}else{
				return $this->name . ", 不符合条件!";
			}
		}
	}

	# 子类继承父类的所有方法
	class Lifting extends BaseObj{
		public function ShowName(){
			return $this->name;
		}

	}

$persion = new BeatBask("lyshark","199");
echo $persion->showMe() . "<br>";             # 默认调用子类的方法
echo $persion->extenShow() . "<br>";          # 继承父类可以调用public的子类
$persion_1 = new Lifting("lyshark","199","1");
echo "来自基类的变量:" . $persion_1->ShowName();
?>

有时我们需要在子类中调用父类中被覆盖的方法,此时我们就可以使用以下方式实现,先调用原始构造函数,然后再增加新的功能,这样子类中就可以直接调用父类中的方法了.

<?php
	class Person
	{
		protected $name;
		protected $sex;
		protected $age;

		function __construct($name="",$sex="none",$age=0)
		{
			$this->name = $name;
			$this->sex = $sex;
			$this->age = $age;
		}
		function say()
		{
			echo "姓名: {$this->name} 性别: {$this->sex} 年龄: {$this->age} ";
		}
	}

	class Student extends Person
	{
		private $school;
		function __construct($name="",$sex="none",$age=0,$school="none")
		{
			// 调用被本方法覆盖的,父类的构造方法
			parent::__construct($name,$sex,$age);
			$this->school = $school;
		}
		function say()
		{
			parent::say();                            // 调用父类中被覆盖的方法
			echo "在: {$this->school} 学校上学 <br>";  // 在原有基础上增加功能
		}
	}

	$student = new Student("lyshark","男",25,"家里蹲");
	$student->say();
?>

面向对象重载: 重载指一个标识符被多个函数名,且能够通过函数的参数个数或参数类型将这些同名函数区分开来,调用不发生混淆,其好处是可实现代码重用,不用为了对不同参数类型或参数个数而写多个函数.

多个函数使用同一个名字,但参数个数参数数据类型不同,调用时虽然方法名相同但根据参数个数或参数的数据类型不同而调整调用不同的函数,这就是重载.

<?php
	class persion
	{
		//  __call = 根据参数调用函数(匿名函数调用,不同参数调用不同函数处理)
		public function __call($name,$argc){
			echo "调用方法名称: " . $name . "<br>";
			echo "参数存在个数: " . count($argc) . "<br>";

			if(count($argc) == 1 and $name == "show")
			{
				echo $this->MyPrintA($argc[0]);
			}
			if(count($argc) == 2 and $name == "show")
			{
				echo $this->MyPrintB($argc[0],$argc[1]);
			}
		}

		public function MyPrintA($x)
		{
			return "调用MyPrintA: 参数x = ". $x . "<br>";
		}
		public function MyPrintB($x,$y)
		{
			return "调用MyPrintB: 参数x = ". $x . "参数y = " . $y . "<br>";
		}
	}

$point = new persion();

$point->show(100);
$point->show(1000,2000);
?>

面向对象接口: PHP中类的继承只能单继承,如果需要多继承那么就需要使用接口技术了,接口是一种特殊的抽象类,使用关键字interface来声明,不能实例化对象.

接口中的方法必须全是抽象方法成员属性必须是常量,所有的权限必须是public且由子类来拓展,使用implements代替extends来实现接口,一个类只能继承一个父类,但是可实现多个接口,如果一个类同时使用了继承父类和实现接口,必须先继承再实现.

<?php
	# 创建一个人类的基类
	class people{
		public function eat(){
			echo "就会吃东西,宅在家里做贡献. <br>";
		}
	}

	# 声明接口 MPopedom
	interface MPopedom{
		const NAME = "lyshark";      # 接口属性必须是常量
		public function popedom();   # 方法必须是public,允许子类继承
	}
	# 声明接口 MPurview
	interface MPurview{
		public function purview();
	}

	# 创建子类Member 实现一个接口 MPurview
	class Member implements MPurview{
		public function purview(){
			echo "当前是在 Member 类中,它继承了MPurview. <br>";
		}
	}

	# 创建子类 Manager 实现多个接口继承 MPopedom,MPurview
	class Manager implements MPopedom,MPurview{
		public function purview(){
			echo "当前在Manager里面,它是一个多继承 purview .<br>";
		}
		public function popedom(){
			echo "当前在Manager里面,它是一个多继承 popedom .<br>";
		}
	}

	# 创建My类,继承people基类,然后再实现多接口继承.
	class My extends people implements MPopedom,MPurview{
		public function purview(){
			echo "当前在My里面,它是一个多继承 purview .<br>";
		}
		public function popedom(){
			echo "当前在My里面,它是一个多继承 popedom .<br>";
		}
	}

$var = new Member();
$var->purview();

$var1 = new Manager();
$var1->purview();
$var1->popedom();

$var2 = new My();
$var2->eat();
$var2->purview();
$var2->popedom();
?>

对象的克隆: 有时我们需要建立一个对象的副本,改变原来的对象时不希望影响副本,此时可使用对象的克隆,即将原对象的所有信息从内存中复制一份,存储在新开辟的内存中用于对象的拷贝,克隆后两个对象互不干扰.

<?php
	class Person
	{
		private $name;
		private $age;

		function __construct($name="",$age="")
		{
			$this->name=$name;
			$this->age=$age;
		}
		function say($count)
		{
			for($x=0; $x<$count; $x++)
				echo "姓名: {$this->name} 年龄: {$this->age} <br>";
		}
	}

$p1 = new Person("admin","22");
$p1->say(3);

$p2 = clone $p1;
$p2->say(4);
?>

上面的程序一共创建了两个对象,由于使用了克隆则两个对象的数据成员以及成员属性是一样的,但如果我们想要在克隆后给克隆对象分配新的成员属性,此时可以使用_clone方法,该魔术方法可在克隆时指定新的参数.

<?php
	class Person
	{
		private $name;
		private $age;

		function __construct($name="",$age="")
		{
			$this->name=$name;
			$this->age=$age;
		}
		// 当被克隆是执行此方法,初始化变量值.
		function __clone()
		{
			$this->name = "none";
			$this->age = "0";
		}
		function say($count)
		{
			for($x=0; $x<$count; $x++)
				echo "姓名: {$this->name} 年龄: {$this->age} <br>";
		}
	}

$p1 = new Person("admin","22");
$p1->say(3);

$p2 = clone $p1;
$p2->say(4);
?>

类中常量与静态变量: 在类中定义变量是添加static修饰,即可定义为静态变量,同样如果增加const关键字则定义为常量.

<?php

// 定义类中静态变量:与静态函数的定义与使用
	class StaticClass
	{
		static $count;

		function __construct(){ self::$count++; }
		static function GetCount() { return self::$count; }
	}

// 在类外可以调用到类内的静态变量
StaticClass::$count = 0;
$res1 = new StaticClass();
$res2 = new StaticClass();

echo "调用类内静态方法: " . StaticClass::GetCount(). "<br>";
echo "第二种调用静态变量: " . $res1->GetCount() . "<br>";

// ----------------------------------------------------------------------
// 定义类中常量:类内定义常量并调用
	class Book
	{
		const BOOK_TYPE = " < PHP开发从入门到入土>";
		public $obj_name;

		function setObjName($name){
			$this->obj_name = $name;
		}
		function getObjName(){
			return $this->obj_name;
		}
	}

$c_book = new Book();
$c_book->setObjName("PHP 类图书");

echo "输出内部常量:" . Book::BOOK_TYPE . "<br>";
echo "输出类名:" . $c_book->getObjName();
?>

定义抽象类: 抽象类就是使用了abstract前缀声明过的方法与类,该类是一种不能被实例化的类,或者说只能包含声明部分,而作为其他类的基类来继承后重写使用,且该类无法被直接被调用,另外如果被final修饰过就是最终版,说明该类既不可被继承,也不能再有子类派生类.

<?php
	// final 定义最终类,该类无法被继承,继承会报错
	final class FinalObj { public $id;}

	// abstract 定义抽象类,该类无法实现功能,只能被继承
	abstract class Abs_Base
	{
		protected $username;
		protected $password;

		function __construct($name="",$pass="")
		{
			$this->username = $name;
			$this->password = $pass;
		}

		// 定义抽象方法
		abstract function service($getName,$Price);
		abstract function GetUser();
	}

	// 定义子类MyBook,并继承抽象类PersionObj
	class MyBook extends Abs_Base
	{
		public function service($getName,$Price)
		{
			echo "书名: {$getName} 价格: {$Price} 元 <br>";
		}
		public function GetUser()
		{
			echo "用户: {$this->username} 密码: {$this->password} <br>";
		}
	}

$var = new MyBook("lyshark","123456");
$var->GetUser();
$var->service("《PHP 入土》",62.67);
?>

Implements 实现多态: 如果需要使用接口中的成员,则需要通过子类去实现接口中的全部抽象方法,但通过类去继承接口时需要使用Implements关键字来实现,并不是使用extends实现,多态类似于接口implements是实现多个接口,,接口的方法一般为空的,,必须重写才能使用.

<?php

interface USB{
	const WIDTH = 5;
	public function load();
	public function stop();
}
class computer{
	function using_usb(USB $u){
		$u->load();
		$u->stop();
	}
}

class Mouse implements USB{
	function load(){
		echo "load mouse success <br>";
	}
	function stop(){
		echo "stop mouse success <br>";
	}
}

class keyboard implements USB{
	function load(){
	echo "load keyboard success <br>";
	}
	function stop(){
		echo "stop keyboard success <br>";
	}
}

class main{
	function using(){
		$c = new computer();
		$m = new Mouse();
		$k = new keyboard();

		$c->using_usb($m);
		$c->using_usb($k);
	}
}

$var = new main();
$var->using();
?>

对象的序列化: 对象也是在内存中实际存储的数据类型,有时候我们需要将对象中的值记录下来,这个过程就叫做对象序列化,通常用于对象需要持续保存,将对象序列化后写入数据库等.

<?php
	// 实现普通数组的序列化
	$array = array("one","two","three","five");
	$json = serialize($array);       // 序列化
	//print_r(unserialize($json));     // 反序列化

	$code = json_encode($array);       // 序列化
	//print_r(json_decode($code,false)); // 反序列化

	// ---------------------------------------------
	// 实现对类的序列化,反序列化
	class Person
	{
		private $name;
		private $age;

		function __construct($name="none",$age=0)
		{
			$this->name = $name;
			$this->age = $age;
		}
		function say()
		{
			echo "我是: {$this->name} 年龄: {$this->age}";
		}
	}

$person = new Person("lyshark",24);
$person_string = serialize($person);                // 序列化
echo "序列化后: {$person_string} <br>";              // 输出序列化后的数据
file_put_contents("c://out.log", $person_string);   // 将序列化后的数据写入磁盘

$load_person = file_get_contents("c://out.log");    // 从磁盘中读出
$person = unserialize($load_person);                // 读入类
$person->say();
?>

魔术方法SET: 该方法的作用是在程序运行过程中为私有属性的成员设置值,它不需要有任何返回值,但需要有两个参数,第一个是传入在为私有属性设置值时的属性名,第二个则是传入要为属性设置的值.

<?php
	class Person
	{
		private $name;
		private $age;
		private $sex;

		// 定义构造函数,并给与默认参数.
		function __construct($name="none",$age=0,$sex="none")
		{
			$this->name = $name;
			$this->age = $age;
			$this->sex = $sex;
		}
		
		// 声明魔术方法,传入两个参数,可以添加过滤条件,过滤一些非法参数
		public function __set($PropertyName,$PropertyValue)
		{
			if($PropertyName == "sex")
			{
				// 条件判断,只允许传入男或者女不能传入其他值,如果传入参数非法直接返回
				if( !($PropertyValue == "男" || $PropertyValue == "女"))
				{
					return;
				}
				// 如果通过验证则直接赋值
				$this->$PropertyName = $PropertyValue;
			}
		}
		
		// 定义打印函数,输出结果.
		public function say()
		{
			echo "姓名: {$this->name} 年龄: {$this->sex} 性别: {$this->age} <br>";
		}
	}

$person = new Person("张三",25,"男");
$person->say();

// 如下是将sex属性名传递给__set方法,set方法内部会判断.
$person->sex = "中性";    // 此时会赋值失败,参数被过滤
$person->sex = "女";      // 赋值成功,不会被过滤
$person->say();
?>

魔术方法GET: 该方法与SET方法类似,如果在类中使用GET,则在外部获取私有属性的值时,会自动调用此方法,返回私有属性的值,同时也可以增加一些条件限制,保证私有属性不会被非法的读取.

<?php
	class Person
	{
		private $name;
		private $age;
		private $sex;

		// 定义构造函数,并给与默认参数.
		function __construct($name="none",$age=0,$sex="none")
		{
			$this->name = $name;
			$this->age = $age;
			$this->sex = $sex;
		}
		
		// 声明魔术方法,传入两个参数,可以添加过滤条件,过滤一些非法参数
		public function __get($PropertyName)
		{
			if($PropertyName == "sex")
			{
				return "保密";
			}
			else if($PropertyName == "age")
			{
				if($this->age >= 20 && $this->age <= 100 )
					//return $this->age;
					return $this->$PropertyName;
				else
					return "不符合";
			}
			else
			{
				// 对其他属性不加任何限制,可以任意读取.
				return $this->$PropertyName;
			}
		}
	}

$person = new Person("张三",40,"男");
echo "姓名: {$person->name} <br>";
echo "性别: {$person->sex} <br>";
echo "年龄: {$person->age} <br>";
?>

魔术方法IsSET/UnSET: 魔术方法isset函数的主要用于测定一个变量是否存在,unset函数则是用来删除指定的变量,其传入参数为要删除的变量名称,如果想要删除测试类中的方法就需要使用类内定义的魔术方法来实现.

<?php
	class Person
	{
		private $name;
		private $age;
		private $sex;

		// 定义构造函数,并给与默认参数.
		function __construct($name="none",$age=0,$sex="none")
		{
			$this->name = $name;
			$this->age = $age;
			$this->sex = $sex;
		}

		// 在对象外面使用isset()测定私有成员属性,并传递到外部.
		public function __isset($PropertyName)
		{
			// 如果传入的是name属性,则禁止其测试
			if($PropertyName == "name")
				return false;
			return isset($this->PropertyName);
		}

		// 使用unset()方法删除私有属性.
		public function __unset($PropertyName)
		{
			// 如果是name属性则禁止删除.
			if($PropertyName == "name")
				return false;
			unset($this->PropertyName);
		}

		// 调用输出函数
		public function say()
		{
			echo "姓名: {$this->name} 年龄: {$this->age} 性别: {$this->sex} <br>";
		}
	}

$person = new Person("张三",25,"男");

var_dump(isset($person->name));   // 禁止测试该变量
var_dump(isset($person->age));    // 可以测试
unset($person->sex);              // 删除变量

$person->say();
?>

魔术方法Call: 当程序试图调用不存在或不可见的成员方法时,PHP会先调用call方法来存储方法名称及其参数,该函数包含两个参数,即方法名和方法参数,其中方法参数是以数组形式存在的.

<?php
	class BaseObj
	{
		public function MyDemo()
		{
			echo "调用方法如果存在,则会直接执行";
		}

		public function __call($method,$parameter)
		{
			echo "方法名为:" . $method . "<br>";

			echo "方法名: {$method} <br>";
			echo "参数列表: "; var_dump($parameter);
		}
	}

$var = new BaseObj();
$var->MyDemo();                      // 调用方法存在则正常执行
$var->MyPrint("how","what","where")  // 不存在则执行call方法
?>

魔术方法callStatic: 当用户调用了一个类中不存在的函数时,默认会触发该函数。

<?php
class BaseObj
{
	static $name= "lyshark";
	function __invoke()
	{
		echo "hello lyshark <br>";
	}
	static function __callstatic($name,$args)
	{
		echo "{$name} 函数没有找到. <br>";
		var_dump($args);
	}

}

$var = new BaseObj();
$var;                           # 触发__invoke方法
BaseObj::hello("lyshark","22"); # 如果方法不存在则会执行__callstatic
?>

魔术方法toString: 当使用echo或print输出对象时,可以自动将对象转换为字符串输出,如果没有该方法,直接输出对象将会发生致命错误。

<?php

class BaseObj{
	private $name = "lyshark";
	public function __toString(){
		return $this->name;
	}
}

$var = new BaseObj();
echo "对象 var 的值是: ". $var;
?>

魔术方法autoload: 该方法可以自动实例化需要使用的类,当程序要用到一个类但没加载时,该方法会在指定路径自动查找该类名称,如果找到程序继续执行,否则会报错。

// 首先创建: BaseObj.class.php
<?php
class BaseObj{
	private $count;
	public function __construct($count){
		$this->count = $count;
	}
	public function __toString(){
		return $this->count;
	}
}
?>

// 接着创建index.php
<?php
function __autoload($class_name){
	$class_path = $class_name.'.class.php';
	if(file_exists($class_path)){
		include_once($class_path);
	}else
		echo "而理性";
}
$var = new BaseObj("hello lyshark");
echo $var;
?>

新常量覆盖旧常量: 基类中定义了一个常量,子类中同样定义相同的常量,则两个常量会发生冲突,覆盖的现象.

<?php
class persion{
	const NAME = "lyshark";
	public function __construct(){
		echo "当前姓名:" . persion::NAME;
	}
}
class per extends persion{
	const NAME="alex";
	public function __construct(){
		parent::__construct();      # 调用父类构造方法
		echo "新的姓名:" . self::NAME;
	}
}

$class_name = new per();
?>

对象之间的比较: 比较对象之间是否有差异,双等于号时比较内容是否一致,三个等于号则是比较引用地址是否一致.

<?php

class SportObj
{
	private $book_type;

	public function __construct($book_type)
	{
		$this->book_type = $book_type;
	}
}

$book = new SportObj("book");
$clonebook = clone $book;
$referBook = $book;

if($book==$clonebook)
{
	echo "book = clonebook 两个对象的内容相等. <br>";
}
if($referBook === $book)
{
	echo "referBook = book 两个对象引用地址相等. <br>";
}
?>

对象之间类型检测: instanceof 操作符可以用于检测当前的对象属于哪个类中的成员.

<?php

class BaseObj
{
	public function init()
	{
		echo "这是基类 <br>";
	}
}

class MyBook extends BaseObj
{
	public function init()
	{
		echo "这是BaseObj的派生类. <br>";
	}
}

$var = new MyBook();
if($var instanceof MyBook)
{
	echo "对象var 属于 MyBook类";
}
?>