PHP高级面对对象
面对对象三大特性: 封装 继承 多态
封装 : 将数据与对数据的操作绑定到一起 对外界隐蔽 对外部提供可以操作的方法
继承: 面对对象思想中实现代码重复利用的重要特性.
多态: 类继承条件下 同时出现方法下的重写 父类对象对于子类对象的引用 让一个对象同时拥有两种状态 PHP中不支持多态.
PHP重载子类对父类属性与方法的访问是__有限__的
子类可以继承父类的共有成员 public
子类可以继承父类的受保护成员 procted 子类内部也可以访问 外部不可以访问
子类可以继承私有属性 private 但是子类与外部都无法访问 除非提供访问接口
子类无法继承父类的私有方法
类常量可以被继承 被访问
<?php
class father{
const PI = 3.1415926;
}
class son extends father{
}
$s = new son();
var_dump($s::PI);//float(3.1415926)
?>
静态成员遵守上面的规则 构造方法与析构方法也可以被继承
当父类有些方法 无法满足子类的需求时 可以重写父类方法 要与父类方法名相同 参数相同 这样系统就会自动识别出重写了父类的方法 需要注意的是__子类重写的方法 控制不能比父类强 只能比父类弱__。
<?php
class father{
const PI = 3.1415926;
protected function show(){
echo __METHOD__,"<br/>";
}
}
class son extends father{
# private function show(); atal error: Non-abstract method son::show() must contain body
public function show(){
parent::show();
echo __METHOD__,"<br/>";
/*father::show
son::show*/
}
}
$s = new son();
$s->show();
?>
parent::
重写的作用是当父类的某些方法无法满足子类的业务时 在子类中对继承来的方法重写 具有特性
当遇到某个方法需要使用到父类的代码 但是还需要添加一些逻辑时 就要用到 parent
parent::(普通方法 )(静态方法)(静态属性)(类常量)可以直接调用 不能调用普通属性 上面的例子中 就是调用了父类的show 方法
静态延迟绑定
<?php
class father{
protected static $NAME ="ada";
public function getName(){
echo __METHOD__.self::$NAME,"<br/>";
}
}
class son extends father{
public static $NAME = "bob";
}
$f=new father();
$s = new son();
$f->getName();
$s->getName();
/*
ather::getNameada
father::getNameada
self 当前类 方法在哪个类 代表哪个类
如果son类重新写了getName方法 $s 调用的getName()方法 sele所指定的就是son
*/
?>
<?php
class father{
protected static $NAME ="ada";
public function getName(){
echo __METHOD__.static::$NAME,"<br/>";
}
}
class son extends father{
public static $NAME = "bob";
}
$f=new father();
$s = new son();
$f->getName();
$s->getName();
/*
father::getNameada
father::getNamebob
都是调用了的父类中的getNanme方法
static 哪个类的对象调配用 代表的是哪个类
*/
?>
静态延迟绑定 static:: 通常放在父类的方法中 当子类继承方法时 可以不通过重写方法也能访问当前类的属性 一定要通过继承之后的子类访问才有效果
FINAL
最终类 : 不可以被继承的类
最终方法 : 不可以被重写的方法
<?php
final class father{
}
class son extends father{
}
#Fatal error: Class son may not inherit from final class (father)
?>
<?php
class father{
protected final function getClass(){
echo __class__;
}
}
class son extends father{
public function getClass(){
}
}
#Fatal error: Cannot override final method father::getClass() i
?>
抽象类
抽象类 : 含有抽象方法的类 只能被继承 不能被实例化 多用于对子类的规范
抽象方法 : 不含有结构体的方法 抽象方法不能私有
继承抽象类的子类必须重写抽象方法
<?php
abstract class father{
protected abstract function getClass();
}
class son extends father{
#要么子类也抽象化 要么重写抽象方法
public function getClass(){
echo __CLASS__;
}
}
$s = new son();
$s->getClass();
?>
接口
抽象类可以规划子类因为继承抽象类的子类必须重写其中的方法 还有一种专门用来规划类内部结构的东西 接口 接口和抽象类类似 但是接口中只允许有抽象方法 (还允许有接口常量 但是很少会遇到 )
一般开发都是第一层由接口规划 然后抽象类 实现接口 同时补充一些其他方法和属性 进一步规划
<?php
interface Human{
#接口中的方法不能被访问限定修饰符修饰
#接口中的方法虽然都是抽象的 但却不需要abstract修饰 默认修饰
function eat();
function sleep();
}
interface animal{
function run();
}
abstract class man implements Human,animal{
#可以实现 implements 多个接口
protected abstract function getCloth();
}
class Ada extends man{
public function eat(){
echo "吃饭";
}
public function sleep(){
echo "睡觉";
}
public function run(){
echo "跑步";
}
public function getCloth(){
echo "穿衣服";
}
}
$s = new Ada();
$s->getCloth();
?>
trait
PHP是一种不支持多继承的语言 如果一个子类想同时拥有两个父类的方法 只能让其中一个父类先下去继承其中一个父类 然后子类在继承这个父类 形成链式继承 这样可以同时拥有两个父类的方法 但是这种方法会使代码十分臃肿
这个时候就可以使用trait 中文特质 trait与class类似 是一种结构体 trait中可以存放属性 方法 但是不能存放类常量 因为本质上trait 不是类
调用trait 的时候直接use traitName;
即可
一个类中可以使用多个trait
<?php
trait t1{
public function show(){
echo __METHOD__;
}
}
trait t2{
public function show1(){
echo __METHOD__;
}
}
class TraitTest{
use t1;
use t2;
}
$t = new TraitTest();
$t->show();
$t->show1();
//t1::show t2::show1
?>
可以使用多个trait就会面临另一个问题 如有两个trait中方法重复怎么办 ?
use t1,t2 { t1::functionName insteadof t2;}
前面特质中的方法 代替后面的方法 对象调用调用的是前面的方法
<?php
trait t1{
public function show(){
echo __METHOD__;
}
}
trait t2{
public function show(){
echo __METHOD__;
}
}
class TraitTest{
use t1,t2{
t1::show insteadOf t2;
t2::show as showInT2;
}
#使用t1 t2 但是用t1::show();的方法替代T2的show方法 这样的话 对象调用show();方法 就是调用t1 的show
# 使用as 将t2中的show 改名
}
$t = new TraitTest();
$t->show();
$t->showInT2();
//t1::show
//1::showt2::show
?>
如果两个特质中的方法都要使用到 就要利用as关键字 将其中一个名字改名 然后在调用
traitName::functionName as newFunctionName;
当trait中的属性与类中的属性相同时 系统会报错 不可以同名
trait 中的方法可以与类中的方法同名 但是类中的方法会覆盖trait的方法
如果类中的方法是继承自父类 trait中的方法会覆盖继承方法
总之类中的方法优先级最大 父类的方法优先级最小
我们还可以利用***as***来更改trait中方法的访问限定修饰符
trait::functionName as 访问修饰限定符 functionName
<?php
trait t1{
private function show(){
echo __METHOD__;
}
}
class TraitTest{
use t1{
t1::show as public shows;
}
}
$t = new TraitTest();
echo $t->shows();
?>
本质上 as 是在内存中复制了一个新方法 并且改变了名称和权限 trait本体的方法并没有改变
traur可以使用抽象方法 实用类必须把自己抽象化或者重写抽象方法
**trait 主要是提供一些公共的方法 让其他类在继承之余还可以访问使用这些方法 实现代码的重复利用 当一个抽象类规范子类之后 但是还需要使用到其他的一些公共方法 但是这些公共方法与抽象类的其他子类关系又不大的时候 就可以使用trait **
PHP重载
重载本来的意思是在一个类中可以出现多个同名的方法 这些方法彼此之间参数的个数,类型不一样 但是php并不支持这种重载
php重载是一种当对象调用某些不存在或者权限不够的方法或者属性时 自动调用的一系列魔术方法 本质上是一种错误处理机制
在看php重载之前选了解一下isset()方法 参数为变量主要是查看变量是否被定义 被定义返回true 没被定义返回false 若变量不存在则返回 FALSE
若变量存在且其值为NULL,也返回 FALSE
若变量存在且值不为NULL,则返回 TURE
empty()
若变量不存在则返回 TRUE
若变量存在且其值为""、0、“0”、NULL、、FALSE、array()、var $var; 以及没有任何属性的对象,则返回 TRUE
若变量存在且值不为""、0、“0”、NULL、、FALSE、array()、var $var; 以及没有任何属性的对象,则返回 FALSE
<?php
class Overloadtest{
private $name ="asda";
#当对象访问不存在或者权限不够的数据的时候会调用
#h会把属性名 当作参数发过来
public function __get($name){
if($name=="name"){
echo "没有权限 但是值是".$this->name,"<br/>";
}else{
echo "数据不存在";
}
}
#当对象设置不能访问或者不存在的属性时调用
#属性名 属性值会作为参数传入
public function __set($name, $value)
{
if($name==("name")){
echo "你没有权限把name设置成".$value,"<br/>";
}else{
echo "属性不存在";
}
}
#外部调用isset 或者 empty 当参数为没有权限访问或根本不存在的属性时 会调用此函数
public function __isset($name)
{
echo "调用了isset方法";
}
#当外部调用了unset函数 且参数为没有权限访问或更本不存在的属性时 会调用此函数
public function __unset($name)
{
echo "调用了__unset方法";
}
#当把对象当作普通变量输出 或者 连接时 触发
#__toString 必须返回一个string类型的值
public function __toString()
{
echo "你所操作的是一个对象 不是基础数据","<br/>";
return "a";
}
}
$s = new Overloadtest();
echo $s->name;//没有权限 但是值是asda
echo $s->age;//数据不存在
$s->name = "bob";//你没有权限把name设置成bob
$s->age =18;//属性不存在
echo isset($s->name);//调用了isset方法
$a = $s."aaa";//你所操作的是一个对象 不是基础数据
echo $s;//a
?>
上买的重载都是由于外部操作了没有权限访问或者根本不存在的属性触发的 下面的重载主要是根据外部调用了类内部 不存在或者其权限不够的方法触发的
<?php
class Overloadtest{
private function get($f){
echo __METHOD__;
}
private static function set(){
echo __METHOD__;
}
#d当外部对象调用了类内部没有或者权限不够的方法时调用
#第一个参数是调用的对象名 第二个参数是方法中的参数 是一个数组的形式
public function __call($name, $arguments)
{
echo $name." ";
var_dump($arguments);
}
#当调用静态方法出错时
#__callstatic必须修饰为 static
public static function __callStatic($name, $arguments)
{
echo $name;
var_dump($arguments);
}
}
$s = new Overloadtest();
//$s->get("asdasd");//get array(1) { [0]=> string(6) "asdasd" }
$s::set("调用静态方法");//set array(1) { [0]=> string(18) "调用静态方法" }
?>
对象遍历
PHP中可以向遍历数组那样遍历对象
<?php
class student{
public $name =10;
protected $age;
private $gender;
public function getName(){
echo $this->name;
}
protected function getAge(){
echo $this->agel;
}
private function getGender(){
echo $this->gender;
}
}
$s = new student();
foreach ($s as $key => $value) {
echo "this key is ".$key."and the value is ".$value;
}
//his key is nameand the value is 10his key is nameand the value is 10
//只能遍历出属性 且遵从访问规则
?>