- 面向对象三大特性
- PHP继承
- 抽象类
- 接口
- 对象遍历
一、 面向对象三大特性
在面向对象思想中,有三大特性:
封装
、继承
和多态
。
1. 封装
定义:封装,字面意思就是将内容装到某个容器中,并进行密封保存。在面向对象思想中,封装指将
数据
和对数据的操作
捆绑到一起,形成对外界的隐蔽,同时对外提供可以操作的接口
。
- 数据:即要操作的数据,在面向对象的具体体现就是类成员属性(属性和静态属性)和类常量,这些都是在类的内部定义的用来保存数据的
- 数据的操作:即数据加工过程,在面向对象的具体体现就是方法,内部定义业务逻辑对数据进行加工处理。
- 捆绑到一起:即使用类结构{}将属性、类常量和方法存放到一起,成为一个整体
- 对外提供可操作的接口:即提供可以供外部访问的类成员(通常是方法)
//封装特性
class Saler{ //捆绑内容到一起,形成对外界隐蔽的整体
const ALLOW = true;
private static $count = 0;
private $money = 0; //数据:属性和类常量保存
public function getMoney(){ //数据操作
return $this->money;
}
}
//外部
$s = new Saler();
$s->getMoney(); //外部只能访问类中公有的方法,具体实现不可见
总结
- 封装是从对象抽象形成类的过程
- 封装是一种将数据和数据操作捆绑到一起,形成对外部的隐蔽,同时提供对外操作的接口
- 封装是面向对象第一大特性,也是面试官喜欢询问对面向对象思想理解的知识
2. 继承
定义:继承,即有些类似的对象中,如人是一种大类,下面可以细分出一些小类,如男人、女人等,然后有一些公共的特性可以在一些基础类中体现,而其他相似或者较小类可以直接使用上级类中的公共代码。
- 继承的基础:子类(要继承其他类的类,也称之为派生类)与父类(被继承类,也称之为基类)之间本身是一种包含于被包含关系,如此才有可继承的前提
//大类
class Human{} //人类
//小类
class Man{}
class Woman{} //属于人类包含的部分,可以继承Human
- 继承关键字:extends,子类想要继承父类,则必须在子类结构申明时明确使用extends关键字来继承相关类
//父类
class Human{} //人类
//子类
class Man extends Human{}
class Woman extends Human{} //继承Human类
- 继承效果:子类可以不用自己去实现某些功能,而可以直接访问父类中已经存在的成员
//父类
class Human{
public function eat(){
echo '吃饭';
}
}
//子类
class Man extends Human{} //子类为空类:没有类成员
//实例化子类对象
$m = new Man();
$m->eat(); //输出吃饭
总结
- 继承extends是面向对象思想中实现代码重复利用的重要特性
- 继承是指子类可以直接访问父类中已经存在的成员
- 继承可以节省代码工作,同时允许子类中进行扩展,即在子类中增加必要的父类不存在的功能
3. 多态
定义:多态,是指在发生类的继承的情况下,同时出现方法的重写(override),即子类拥有与父类同名的方法。然后在实例化对象的时候让父类对象指向子类对象(强制类型,PHP不支持),父类对象表现的子类对象的特点。
- 多态需要强类型语言,以Java为例
//父类
class Animal{
public void show(){
System.out.println("Animal");
}
}
//子类
class Dog extends Father{
//重写show方法
public void show(){
System.out.println("Dog");
}
}
//实例化:Java是强类型,必须指定保存数据的变量的类型
Animal a = new Dog(); //父类对象指向子类对象空间
a.show(); //打印Dog,父类对象调用的是子类方法
- PHP是弱类型语言,所以不存在变量的强制类型,因此PHP不支持多态。
总结
- 多态的发生必须是有继承关系,并且子类要重写父类方法
- 多态是指父类对象拥有子类形态,并且可以表现出子类的特性(调用子类方法)
- PHP是弱类型语言,不支持多态
二、 PHP继承
1. 继承相关概念和实现
定义:继承extends,是指子类通过继承可以访问父类的成员。
- 继承基本语法:class 子类 extends 父类{}
//父类(基类)
class Human{}
//子类(派生类)
class Man extends Human{}
- 继承效果:父类被继承的内容,可以通过子类对象进行访问(只能是子类对象)
//父类(基类)
class Human{
public function showName(){
echo __CLASS__;
}
}
//子类(派生类)
class Man extends Human{}
//实例化子类对象:如果实例化父类对象Human,那么与子类和继承毫无关系
$m = new Man();
$m->showName(); //访问继承自父类的方法
- 继承目标:继承本质是针对同类有包含关系的共性继承,即父类通常是包含子类,子类属于父类。所以在父类中通常定义的是子类共有的一些特性成员,这是开发者默认遵循的规则
class Animal{}
class Man extends Animal{} //不会有语法错误,也可以继承,但是不符合实际
总结
- 继承是利用extends进行子类和父类的关联
- 继承利用extends关键字实现
- 继承是指实现继承关系后,子类对象可以访问父类被继承的成员。而父类对象不可以访问子类成员(没有关系)
2. 有限继承
定义:有限继承,指子类在继承父类的成员的时候,并非继承所有内容,而是继承并使用父类部分内容。
- 继承内容:PHP中继承是子类继承父类所有的公有成员、受保护成员和私有属性,不能继承父类的私有方法
//父类
class Human{
const CALL = '人';
public $name = 'human';
protected $age = '100';
private $money = '100';
public function showName(){
echo $this->name;
}
protected function showAge(){
echo $this->age;
}
private function showMoney(){
echo $this->money;
}
}
//子类
class Man extends Human{}
//实例化子类
$m = new Man();
var_dump($m); //可以看到父类私有属性
$m->showName(); //允许直接访问:方法为公有允许类外访问
- 受保护继承protected,protected关键字的产生本身就是纯用于继承的,表示允许被子类在子类内部访问的意思,而不允许被外部直接访问。
//父类
class Human{
protected $age = '100';
protected function showAge(){
echo $this->age;
}
}
//子类
class Man extends Human{
//在子类内部增加公有访问访问继承自父类的受保护成员
public function getProtected(){
echo $this->age; //访问父类受保护属性
$this->showAge(); //访问父类受保护方法
}
}
//实例化子类对象
$m = new Man();
$m->getProtected(); //正确输出:说明可以访问
- 访问父类私有成员:子类若想访问父类私有成员,那么前提是父类提供了能够访问私有成员的接口:即提供了公有或者受保护的方法给子类访问
//父类
class Human{
private $age = 100;
private $money = 100;
//提供接口供子类访问:此时通常是受保护的方法,肯定不允许外部直接访问的
protected function getAge(){
echo $this->age;
}
}
//子类
class Man extends Human{
//依然需要定义方法来访问继承自父类的受保护的成员
public function showPrivate(){
$this->getAge();
}
}
//实例化子类对象
$m = new Man();
$m->showPrivate(); //输出100,表示正确访问
注意:虽然子类可以通过以上方式来实现访问父类的私有成员(包括私有方法),但是从设计的意义上讲,私有就是不允许外部访问,所以父类通常不会提供对外的访问接口,以上案例只是为了知识说明。
- 静态成员(类常量)也遵循继承规则(PHP继承本质是对象),只是访问方式是由类进行访问
class Human{
const NAME = '人';
public static $count = 0;
protected static $type = array('黑','黄','白');
public static function getCount(){
echo self::NAME;
echo self::$count;
}
protected static function getType(){
print_r(self::$type);
}
}
class Man extends Human{
//依然需要子类中访问受保护成员
public static function getHuman(){
Human::getType();
}
}
echo Man::$count; //允许直接访问
Man::getCount(); //访问父类静态方法
Man::getHuman(); //利用子类公有方法访问父类受保护成员
- 构造方法和析构方法也可以被子类继承,此时需要注意子类对象实例化时对应的父类构造方法的参数
//父类
class Human{
private $money;
public function __construct($money){
$this->money = $money;
}
public function __destruct(){
echo 'die';
}
}
//子类继承
class Man extends Human{}
//子类实例化:自身是空类,没有指定构造方法
//$m = new Man(); //错误:缺少参数,因为会自动调用父类构造方法
$m = new Man(100); //正确
总结
- 继承是有限继承,理论上是用来继承父类允许被继承的部分,即使用public或者protected修饰的成员
- 因为对象的属性是保存在对象内存空间,所以父类的私有属性也会继承
- 父类私有成员本质不允许被子类访问,但是可以通过父类开放接口实现(一般不会这么操作)
- 静态成员也可以遵循继承规则
- 构造方法也可以被继承,因此在实例化子类对象的时候,要考虑到父类构造方法所使用到的参数问题
3. 重写Override
定义:重写,即子类中定义了与父类重名的成员,子类可以重写父类任意类成员,通常重写是用来重写父类的方法,用于扩展或者更改某些业务逻辑。
- 子类继承父类,同时子类定义与父类同名的类成员
//父类
class Human{
public $name = 'Human';
public function show(){
echo __CLASS__,'<br/>';
}
}
//子类继承
class Man extends Human{
//定义同名属性
public $name = 'Man';
//定义父类同名方法
public function show(){
echo __CLASS__,' hello world<br/>';
}
}
- 重写父类成员之后,子类只会直接访问子类的成员(覆盖)
//接上述代码
//实例化子类对象
$m = new Man();
$m->show(); //输出Human hello world
var_dump($m); //只有子类属性$name
注意:不管是公有和是受保护属性,一旦重写,父类的就会不存在,而私有属性不会被覆盖而丢失
- 重写的要求1:子类重写父类的方法,控制权不能高于父类,即子类可以比父类更开放
//父类
class Human{
protected function show(){
echo __CLASS__,'<br/>';
}
}
//子类继承
class Man extends Human{
//重写
protected function show(){} //正确
public function show(){} //允许
private function show(){} //错误:控制权比父类更严格
}
- 重写的要求2:PHP中重写要求子类重写父类方法的时候,必须保证与父类同名方法参数一致
//父类
class Human{
protected function show(){
echo __CLASS__,'<br/>';
}
}
//子类继承
class Man extends Human{
//重写
public function show(){}
public function show($a){} //错误,与父类同名方法不一致
}
注意:在方法参数一致不单单要求数量一致,而且数据类型要求也必须相同,但形参名字可以不同;另外,在PHP7以前重写对于参数这块没有要求。
- 重写的要求3:重写针对的是被继承的成员,父类私有方法不会被继承,因此不受要求2规定
//父类
class Human{
private function show(){
echo __CLASS__,'<br/>';
}
}
//子类
class Man extends Human{
private function show($name){ //不会报错,因为本质不存在重写(父类Human::show没有被继承)
echo $name,'<br/>';
}
}
- 重写是指子类拥有特殊的情况,一般是需要在父类的基础上进行扩展,此时如果想要继续保证父类被重写的方法继续执行(默认永远只访问子类重写的新方法),需要在子类重写方法的时候使用parent关键字
//父类
class Human{
protected function show(){
echo __CLASS__,'<br/>';
}
}
//子类继承
class Man extends Human{
//重写
public function show(){
//强制调用父类被重写方法
parent::show();
//扩展业务逻辑
echo __CLASS__,'<br/>';
}
}
注意:parent不能访问父类的属性,可以访问静态属性、静态方法、类常量和普通方法
总结
- 重写override是一种在子类中定义父类同名成员的操作
- 公有、受保护的属性重写是直接覆盖父类成员,私有属性不会被覆盖;公有、收保护的方法会被重写,但是私有方法不会被重写(私有方法本质没有被继承)
- 重写的要求
- 子类控制权不能高于父类控制权
- PHP7中要求被重写的方法必须与父类保持参数一致(数量和类型)
- 方法被重写后,访问调用的都是子类方法,如果想要访问父类方法,可以通过在子类方法中使用parent关键字来强制访问父类方法
- parent不能用于访问父类的属性(静态属性可以)
4. PHP继承特点
定义:PHP继承与其他纯面向对象(从设计之初就完全由面向对象思维支配)编程语言是有一些不一样的。
- PHP中继承只能单继承:即子类只有一个父类(有些语言支持多继承)
class Man{}
class Woman{}
class Ladyboy extends Man,Woman{} //PHP中错误,不允许继承多个父类
- PHP若想继承多个类,可以使用链式继承
class Man{}
class Woman extends Man{}
class Ladyboy extends Woman{} //Ladyboy包含了Man和Woman类中所有可继承的成员
- PHP中继承只有私有方法不能被继承
- PHP允许子类继承父类的构造方法和析构方法
总结:PHP中的继承与传统的面向对象继承有着一些小区别,大家在使用继承的时候要严格遵守PHP的继承规则
5. 静态延迟绑定
定义:静态延迟绑定,即在类内部用来代表类本身的关键字部分不是在类编译时固定好,而是当方法被访问时动态的选择来访者所属的类。静态延迟绑定就是利用
static
关键字代替静态绑定self,静态延迟绑定需要使用到静态成员的重写
- 静态延迟绑定:使用static关键字代替self进行类成员访问
//父类
class Human{
public static $name = 'Human';
public static function showName(){
//静态绑定
echo self::$name,'<br/>';
//静态延迟绑定
echo static::$name,'<br/>';
}
}
//子类
class Man extends Human{
//重写父类静态属性
public static $name = 'Man'; //静态属性因为存储在类内部,因此不会覆盖
}
//子类访问
Man::showName(); //输出Human和Man
- 静态延迟绑定一定是通过继承后的子类来进行访问才有效果
//接上述代码
Human::showName(); //输出Human和Human(此时没子类的事儿)
总结
- 静态延迟绑定是指通过static关键字进行类静态成员的访问,是指在被访问时才决定到底使用哪个类
- 静态延迟绑定对比的是静态绑定self
- 静态延迟绑定的意义是用来保证访问的静态成员是根据调用类的不同而选择不同的表现
6. 最终类Final
定义:最终类,使用final关键字修饰类名,表示此类不可以被继承。
- 基本语法:final class 类名
//最终类
final class Man{}
- 最终类无法被继承
//最终类
final class Man{}
class Man18 extends Man{} //致命错误:无法从final类继承
- final关键字不止修饰类表示类不可被继承,还能修饰方法,表示方法不能被重写
//父类
class Human{
public function show(){} //普通方法
public final function walk(){} //最终方法
}
//子类
class Man extends Human{
//重写
public function show(){} //没问题
public function walk(){} //致命错误:不能重写父类中的最终方法
}
总结
- final关键字修饰的类表示无法被继承
- final关键字还可以修饰方法,表示方法不能子类重写(通常类不会使用final关键字)
- final修饰类表示不希望类再出现子类,可以很好保护类的内部结构不被暴露
- final修饰方法表示不希望方法被修改,可以在一个更高的维度来保证同类事务的共同表现
7. 抽象类Abstract
定义:抽象类,使用abstract关键字修饰的类,表示该类只能被继承,不能被实例化
- 基本语法:使用abstract关键字修饰类
//抽象类
abstract class Human{}
- 抽象类无法被实例化
//抽象类
abstract class Human{}
$h = new Human(); //致命错误,抽象类不能被实例化
- 抽象类只能被继承
//抽象类(父类)
abstract class Human{}
//子类
class Man extends Human{} //正确
- abstract关键字还可以用来修饰方法(抽象方法),abstract修饰的方法不能有方法体,而且有抽象方法的类必须声明为抽象类
//抽象方法抽象类
abstract class Human{
//定义抽象方法:没有方法体
abstract public function eat();
public function show(){} //普通方法有方法体
}
- 抽象方法因为要被子类继承实现,所以不能使用private修饰(私有方法不会被继承)
//抽象类
abstract class Human{
//抽象方法
abstract private function eat(); //错误:抽象方法不能私有化
}
- 子类继承抽象类后,如果抽象类中有抽象方法,那么子类必须选择自己成为抽象类或者实现抽象方法(所有抽象方法)
//抽象方法抽象类(父类)
abstract class Human{
//定义抽象方法:没有方法体
abstract public function eat();
public function show(){} //普通方法有方法体
}
//子类1:抽象类继承抽象类
abstract class Man extends Human{} //正常继承
//子类2:子类实现父类所有抽象方法
class Boy extends Man{
//实现从祖父类继承的eat抽象方法
public function eat(){
echo 'eat';
}
}
总结
- 使用abstract修饰的类叫做抽象类
- 抽象类不可以被实例化,只能被继承
- 因为抽象类无法被实例化,因此私有成员在类中没有实质意义(还需要额外提供受保护或者公有方法来实现访问)
- 抽象类的目的是用来规范子类(通常必配抽象方法)
- abstract还可以修饰方法,称之为抽象方法:抽象方法所在的类必须是抽象类,抽象方法不能有方法体
- 有抽象方法的抽象类被继承时子类要么自身是抽象类,要么实现所有抽象方法
- 抽象类这种结构管理,需要耗费较多的架构和初始代码,通常在比较大型或者规范的项目中才会使用
8. 接口Interface
定义:接口,使用interface关键字定义,与类类似,专门用来规范一些共性类必须实现的方法
- 接口定义:使用interface关键字,后跟接口名字(与类结构一样)
interface Human{}
- 接口不是类,不可以被实例化
new Human(); //致命错误,接口不能被实例化
- 接口实现:接口是用来规范类必须完成的事情,所以接口只能被类实现:implements
class Man implements Human{}
- 接口成员:接口中只能定义公有抽象方法和接口常量
interface Human{
//接口常量
const NAME = '人';
//接口抽象方法
public function eat();
//错误示例
public function go(){} //错误:接口中的方法必须为抽象
public $age; //错误:接口中不能有属性
public static $count = 0; //错误:接口中不能有静态属性(成员属性)
protected function walk(); //错误:接口方法必须为公有抽象方法
}
- 接口成员方法必须被实现的子类实现或者类为抽象类,接口常量可以直接在实现类中方法
interface Human{
//接口常量
const NAME = '人';
//接口抽象方法
public function eat();
}
//实现接口
class Man implements Human{
//必须实现接口所有抽象方法
public function eat(){
echo self::NAME; //可以访问接口常量
}
}
//抽象类实现接口
abstract class Ladyboy implements Human{} //正常实现
- 实现接口的类成员,不允许重写接口中的常量,不允许增加接口方法的控制权限
//接上述代码
class Woman implements Human{
//重写接口常量
const NAME = '女人'; //错误:不允许重写接口常量
//强化接口方法控制
private function eat(){} //错误:接口方法不允许使用其他访问修饰限定符,必须使用public
}
- 接口可以继承接口:extends,而且接口可以多继承接口
interface Human{
public function walk();
}
interface Animal{
public function eat();
}
//单继承
interface Man extends Human{}
//多继承
interface Ape extends Human,Animal{}
总结
- 接口是使用interface关键字定义,与类结构类似
- 接口只能被类实现
- 接口成员只有两种:接口常量和公有抽象方法(没有方法体:不需要abstract关键字)
- 类实现接口的时候,必须实现接口中所有的抽象方法(或者抽象类实现)
- 接口中的成员不能被实现接口的类进行重写(接口常量)和权限更高(受保护或者私有化接口方法)
- 接口可以继承接口,而且可以多继承
- 接口是在更大型的项目中,为了保证底层的实现而设定的规范,通常是抽象类实现接口,增加必要成员,然后让实际业务类去继承抽象类。
9. trait代码复用【掌握】
定义:
Trait
是为类似 PHP 的单继承语言而准备的一种代码复用机制。trait可以使得单继承语言拜托为了复用而不得不继承的尴尬,让面向对象变得更加纯粹。
- trait是一种类似class的关键字
trait Eat{
}
- trait内部可以拥有一个类能拥有成员属性(包含静态),成员方法(包含静态),但不能有类常量
trait Eat{
public $time;
protected $how; //允许定义,但是实际不用
private $info;
public function showTime(){
echo $this->time;
}
protected function showHow(){ //允许定义,但是实际不用
echo $this->how;
}
const PI = 3.14; //错误:trait中不能有常量
}
- trait是用来实现代码的复用的,不可以被实例化也不可以被继承(不是类)
trait Eat{}
new Eat(); //错误,没有该类
- trait是用来将公共代码提供给其他类使用的,而类要使用trait的前提是加载对应的trait
trait Eat{
public function show(){
echo 'eat';
}
}
//类中加载trait
class Human{
//加载:使用use关键字
use Eat; //use就表示将trait Eat中的所有东西拿到了当前类Human中
}
//使用trait中的内容
$h = new Human();
$h->show(); //eat:Human类自己没有show方法,但是因为使用了trait Eat,所以可用
- 一个类可以使用多个trait
trait t1{
public function eat(){
echo 'eat';
}
}
trait t1{
public function sleep(){
echo 'sleep';
}
}
class Human{
//使用多个trait
use t1,t2;
public function show(){
$this->eat();
$this->sleep();
}
}
$h = new Human();
$h->show(); //eat sleep
- 如果同时引入的多个trait中有同名方法,那么会产生冲突:解决冲突的方法是使用insteadof代替处理以及对被替代方法使用别名
trait t1{
public function eat(){
echo 't1,eat';
}
}
trait t2{
public function eat(){
echo 't2,eat';
}
}
class Human{
use t1,t2; //错误:eat()方法冲突
}
//解决方案:明确替代
class Person{
use t1,t2{ //花括号
t2::eat insteadof t1; //t2的eat代替t1的eat
}
}
$p = new Person();
$p->eat(); //t2,eat
//解决方案:先替换后别名
class Animal{
use t1,t2{
t1::eat insteadof t2; //明确使用t1中的eat方法
t2::eat as eat2; //t2中的eat方法改名叫eat2
}
}
$a = new Animal();
$a->eat(); //t1,eat
$a->eat2(); //t2,eat
- 同名覆盖问题:如果类中有与引入的trait同名成员,会有不同处理
- 属性:不允许重名,即类中不允许定义与trait中同名的成员属性(静态属性也一样)
- 方法:类覆盖trait
trait Eat{
public $food = '米饭';
public function show(){
echo $this->food;
}
}
class Human{
use Eat;
//定义同名属性
//public $food = '面条'; //错误
//定义方法
public function show(){
echo 'show';
}
}
$h = new Human();
$h->show(); //show:类覆盖掉trait
- 继承覆盖问题:如果类中在使用trait的同时,也是继承自父类,而trait中与父类中有同名方法,那么trait中将覆盖父类同名方法;如果要访问父类方法,可以在trait同名方法中使用parent关键字访问父类同名方法
trait Eat{
public function eat(){
echo 'Eat::eat';
}
}
class Human{
public function eat(){
echo 'Human::eat';
}
}
//子类继承父类同时使用trait
class Man extends Human{
use Eat;
}
$m = new Man();
$m->eat();
- 另外,trait自己不能访问,只是用来给其他类提供代码复用的,因此允许类在使用trait时更高里面方法的访问控制权:在as之后,使用目标访问修饰限定符
trait Eat{
private function show(){
echo 'eat';
}
}
class Human{
use Eat{
show as public eshow;
//注意:as是用来设定别名的,虽然没有同名show,但是系统认为show已经存在,所以必须别名
}
}
$h = new Human();
$h->eshow(); //eat
- trait中可以使用抽象方法,用来规范使用类必须实现对应抽象方法:使用类要么为抽象类,要么就必须实现抽象方法
trait Eat{
public function eat(); //抽象方法
}
abstract class Human{
use Eat; //抽象类:可以不实现抽象方法
}
class Animal{
use Eat;
public function eat(){ //具体类:实现抽象方法
echo 'Animal::eat';
}
}
总结
- trait是一种类似class结构关键字,trait不能被实例化,可以拥有所有类成员结构(类常量不行)
- trait是用来实现代码复用的,为其他类提供公共代码(方法),其他类如果使用trait用use关键字引入
- 在类中use具体trait就相当于将trait内的所有代码在类中写了一遍
- 一个类可以使用多个trait,但是要注意同名问题
- 同名方法可以使用insteadof来实现替代:一个trait中的同名方法替代另外一个,类就访问替代的那个
- 同名方法可以在被替代之后使用as制作方法别名:类就可以拥有两个方法
- 类中在引入trait后,要注意与trait中的同名成员问题
- 同名属性:不予许(类中不允许)
- 同名方法:允许,类中的方法会覆盖trait中的方法
- 如果类在使用trait的同时也继承了其他类,那么trait中出现的同名方法会覆盖基类的同名方法:类自己 > trait > 基类
- 类在使用trait时可以修改trait方法的控制级别:更严或者更宽松都可以,注意修改控制级别时使用的是别名机制,一定需要改成别名:[trait名::]方法名 as 访问修饰限定符 别名;(原来trait中的方法依然可以正常使用)
- trait中可以使用抽象方法,那么使用该trait的类就必须本身为抽象类或者将抽象方法实现
- trait使用机制
- 有公共代码要实现(方法),而这些方法可能在很多类中会用到
- 公共代码不是属于某一类事务特有,而是很多事务都有(不符合继承)
10. PHP重载
定义:重载overload,本意指在一个类中可以出现多个同名方法,彼此之间的参数个数和类型不一样。但是PHP中不支持同名方法,而且也不区分数据类型(弱类型语言),所以PHP不支持传统重载。PHP中的重载指的是当某些不允许操作发生时,会自动调用的一种内部机制,即自动调用相关的魔术方法。
- 魔术方法:指系统为类中预先设计好的,只需要开发者实现的方法,魔术方法以双下划线
__
开始。对象在某些特定情况下会自动调用的方法。构造方法、析构方法和克隆方法就是魔术方法
class Man{
public function __construct(){}
}
new Man(); //实例化后对象自动调用(触发时机:对象被实例化)
- PHP重载是指某些容错处理(也可以理解为为了某些特殊情况而自动调用),在访问没有权限或者不存在的属性或者方法的时候,会自动触发的魔术方法。
- 属性重载
- 方法重载
- 属性重载:当PHP对象访问不存在的或者没有权限访问的属性的时候会自动调用的方法
- __get($key):读属性的时候触发
- __set($key,$value):写属性的时候触发
- __isset($key):外部调用isset()函数或者empty()函数时自动触发
- __unset($key):外部调用unset()结构删除对象属性时自动触发
- __toString():对象被当做普通变量输出或者连接时自动调用
class Man{
private $age = 10;
//读取重载
public function __get($key){
echo $key,__METHOD__,'<br/>';
}
//写重载
public function __set($key,$value){
echo $key . ' : ' . $value . '<br/>';
}
//查是否存在重载
public function __isset($key){
echo $key,__METHOD__,'<br/>'
}
//删除属性重载
public function __unset($key){
echo $key,__METHOD__,'<br/>';
}
//对象字符串化
public function __toString(){
echo __METHOD__,'<br/>';
}
}
//实例化
$m = new Man();
$m->age; //类外访问私有属性:原本不允许
$m->age = 100; //设置
isset($m->age); //判定
unset($m->age); //删除
- 属性重载的目的:一方面为了不让程序运行出错,另一方面可以在类内部由我们自己控制内容的访问
class Man{
private $age = 10;
//读取重载
public function __get($key){
//echo $key,'<br/>';
//定义一个允许访问列表:假设有很多私有属性
$allow = array('age');
//判定是否在列表内:在就允许访问,不在就返回NULL或者false
if(in_array($key,$allow)){
return $this->$key; //可变属性:$key是外部访问的目标,最终为$this->age
}
//不允许访问
return false;
}
//写重载(该方法没有返回值)
public function __set($key,$value){
//echo $key . ' : ' . $value . '<br/>';
//与__get理论类似:允许的设置,不允许的不设置(什么都不做即可)
}
//判定重载
public function __isset($key){
//给出内部判定结果
return isset($this->$key);
}
//对象重载
public function __toString(){
//返回一个指定字符串(一般是当类有属性保存某些信息时,输出某个属性)
return __METHOD__;
}
}
- 方法重载:当PHP对象访问不存在的方法或者不允许访问的方法时自动调用的方法(抑或是谋者特殊情况,如构造方法)
- __call($function_name[,$args]):对象调用不可调用方法时触发
- __callStatic($function_name[,$args]):类访问不可调用静态方法时触发
class Man{
private function show(){
echo __METHOD__,'<br/>';
}
private static function staticShow(){
echo __METHOD__,'<br/>';
}
//普通方法重载
public function __call($name){
echo $name,__METHOD__,'<br/>';
}
//静态方法重载
public static function __callStatic($name){
echo $name,__METHOD__,'<br/>';
}
}
//访问不可访问的方法
Man::staticShow();
$m = new Man();
$m->show();
- 方法重载的主要目的:不让外部访问出错。当然,如果必要时也可以进行内部访问
class Man{
private function show(){
echo __METHOD__,'<br/>';
}
private static function staticShow(){
echo __METHOD__,'<br/>';
}
//方法重载
public function __call($name,$arg){
//允许访问列表
$allow = array('show');
//判定是否在列表中
if(in_array($name,$allow)) return $this->$name($arg);
//其他情况
return false;
}
public static function __callStatic($name){
//不允许访问
return false;
}
}
总结
- PHP重载不是指同名方法,而是指对象或者类在访问一些不允许或者不存在的属性或者方法的时候自动调用的魔术方法
- PHP重载分为属性重载和方法重载
- PHP重载的目的是为了保护程序的正确运行而提供的一种容错机制
- 并非所有类都需要实现这些重载,只是如果有类需要对外提供访问使用的时候才有必要采取
11. 对象遍历foreach
定义:遍历对象,其实就是指将对象中的所有属性(公有属性)以键值对的形式取出并进行访问
- 使用foreach对对象进行遍历
//定义类
class Man{
public $name = 'LiLei';
public $height = 178;
public $weight = 140;
protected $age = 30;
private $money = 1000;
}
//实例化
$m = new Man();
//遍历
foreach($m as $k => $v){
echo $k . ' : ' . $v . '<br/>'; //$k为属性名,$v为属性值
}
总结
- foreach可以对对象像数组一样遍历
- foreach遍历对象遍历的是对象内部的所有公有属性(在类外部进行对象遍历)