PHP基础知识点【十】面向对象

面向对象程序设计(Object Oriented Programming,简称OOP)是一种计算机编程架构。面向对象思想的核心:计算机模拟现实世界,解决现实世界的问题。注意:面向对象思想很重要,其次是编程语言的语法。相比于面向过程,两者思想方式不同,面向过程注重功能,怎么一步一步去实现,其程序基本单位大多是函数组成的;而面向对象注重对象,是谁去做这个事情,也就是行为以及状态,其程序基本单位是对象,对象是通过类的实例化产生的。自PHP5起完全重写了对象模型以得到更佳性能和更多特性,PHP5具有完整的对象模型。但是PHP并不是一个纯面向对象的语言,它既可以采用传统面向过程进行编程,也可以使用面向对象设计程序。

目录

【1】类的基本定义-关键词(class)

【2】类实例化对象-关键词(new)

【3】访问成员属性以及方法 -访问符(->) 

【4】特殊对象的引用-关键词(this)

【5】构造方法(__construct())

【6】析构方法(__destruct())

【7】封装性-关键词(public,protected,private)

【8】继承性-关键词(extends)

【9】访问控制-关键词(public,protected,private)

【10】类常量-关键词(const)

【11】关键字static

【12】关键字final

【13】关键字this,self,parent,static

【14】关键字instanceof

【15】抽象类-关键词(abstract)

【16】接口interface与实现接口implements

【17】多态性


【1】类的基本定义-关键词(class)

        问什么要定义类,想要对应的实例对象,通过规范类的属性和方法从而获取符合要求的对象。

        想要对象前,要有类定义,每个类的定义都是以关键词Class开头,后面类名,后面跟着一对花括号,里面包含有类的属性方法的定义。那么类名实际上和变量名、函数名这些命名规则很相似,都需要遵守PHP中自定义名称的规则,如果由多个单词组成,一般每个单词字母开头都大写,类名最好也要见名知其意。一般类的声明从类的属性和类的方法来考虑,不过也可以什么也没有。

        类的属性,就相当于这个类的变量,就是成员属性,举个例子,人类的属性就有姓名,年龄等属性,所以这个属性一般描述这个类的基本特征;属性声明是由关键字 publicprotected或者 private开头,设定其属性的访问控制可见性,然后跟一个普通的变量声明来组成。PHP5为了向后兼容仍然可以用var声明属性,被认为是public修饰。PHP属性中的变量可以初始化,但是初始化的值必须是常数,这里的常数是指 PHP 脚本在编译阶段时就可以得到其值,而不依赖于运行时的信息才能求值。

       类的方法,就相当于这个类的函数,也就是成员方法,例如人类可以学习,说话等行为,所以这个方法一般描述这个类的行为。也就是这个类的属性和方法都是和这个类有关的,不要定义一些和类无关的,虽然程序不会报错,但是不符合面向对象思想。方法的声明也是由关键字 publicprotected或者 private开头,设定其方法的访问控制可见性,后面跟一个函数。

//类的基本定义
class Person{#定义类,用class后面 类名
    public $name;#定义人类的属性,姓名
    public function study(){#定义人类的方法,学习
        echo 'OOP';
    }
}

【2】类实例化对象-关键词(new)

       面向对象程序基本单位就是对象,对象是通过类实例化产生的,所以明白,先有类定义才有对象。利用关键词new后面跟上类名,如果实例化的时候需要传入参数(可能是构造函数传参)可以带上小括号,当然也可以省略。另外如果定义的类属于一个命名空间,那么需要是使用完整的名称。

$tacks1 = new Person;#创建一个Person的对象 引用名为$tacks1;
$tacks2 = new Person();#再创建一个Person的对象,引用名为$tacks2;
$class  = 'Person'; #还记得我们之前说的可变变量,利用一个变量存放类名
$tacks3 = new $class;#也可以进行船舰
var_dump($tacks1,$tacks2,$tacks3);
/*
object(Person)[1]
  public 'name' => null
object(Person)[2]
  public 'name' => null
object(Person)[3]
  public 'name' => null
*/

       一个类可以实例化多个对象,并且对象之间是互相独立的,相当于再内存中开辟了三份空间存放每个对象。但是这三个对象的类型都是同一个类型Person类。

      对象类型的数据是一种占用空间比较大的类型,而且长度总是不确定的,一般对象被创建后都是被存放到堆内存中的,但是对象的引用名称是放在栈里的。例如($tacks1 = new Person;),等号左边为对应的引用;右边是真正的实例对象,放在堆空间中。这样在程序运行的时候,栈中的数据可以直接进行存取,堆中的数据则是通过对象的引用名称进行访问。

【3】访问成员属性以及方法 -访问符(->) 

      我们知道怎么创建对象了,那么如何去访问这些对象的成员属性和成员方法。一般可以通过对象的引用名后面加上一个特殊的运算符号"->"来完成对象成员的访问。

     可以看一下,下面的例子,我定义一个Person的类,其属性eyes默认就直接赋值为2,还有一个study()成员方法。我通过new实例化后,利用->访问其属性和方法。

class Person{
    public $eyes = 2;
    public function study(){
        echo 'I study OOP!';
    }
}
$tacks = new Person;
echo $tacks->eyes,'<br/>';
$tacks->study();

【4】特殊对象的引用-关键词(this)

       我们知道在类外利用实例化对象去访问其属性和方法,但是在对象内部我们如何使用自己的属性或者方法?这里我们可以用到$this,可以在对象内部来访问,相当于代表自己。

class Person{#定义一个类
    public $name;
    public function intro(){
        echo 'I study OOP!'.$this->getName();#调用自己的getName()方法
        #echo 'I study OOP!',$this->getName();#调用自己的getName()方法
    }
    public function getName(){
        echo 'My name is '.$this->name.'<br/>';#调用对象自己的name属性
    }
}
$tacks = new Person;
$tacks->name = 'Tacks';//对对象tacks的属性name进行赋值
$tacks->intro();//调用对象的intro方法 
这里输出结果有点小奇怪,按道理应该先输出I study OOP,但是结果是先执行了getName()。上面我用的.连接符,如果改用逗号就是我正常的结果。
//My name is Tacks
//I study OOP!

【5】构造方法(__construct())

      我们看到,上面一个例子中,实例化对象后,还需要自己手动对属性进行赋值。那么实际上每个类中都会有一个叫做构造方法特殊的成员方法,上面咱们并没有声明,实际上默认会存在一个没有参数列表并且内容为空的构造方法。这个构造方法,在每次创建对象的时候,也就是new一个实例化对象,会自动调用构造方法,同样它也算是类的方法,但是无法用对象直接进行调用。它主要的工作就是为了完成实例化对象前的一些操作。

     声明构造方法和普通声明方法类似。我们用__construct()进行声明。具有构造函数的类会在每次创建新对象时先调用此方法,所以非常适合在使用对象之前做一些初始化工作。那么我们就可以在对象实例化前对属性进行赋值操作,注意如果需要传入参数,那么实例化类的时候,后面要加小括号传入参数。

     另外为了向后兼容方法名与类名一样也是表示构造方法,但是我们不常用这种同名的,因为万一类名改变了,构造方法的类名也要统一修改。 而且后期如果使用了命名空间,这种声明构造方法将不会被使用。因此不建议使用与类名一样的方法。

class Person{
    public $name;
    public function __construct($name){
        $this->name = $name;//实例化对象的前进行赋值。
    }
    public function intro(){
        echo 'I study OOP! My name is '.$this->name;
    }
}
$tacks = new Person('Tacks');#注意要加小括号向构造函数进行传入参数。
$tacks->intro();//I study OOP! My name is Tacks

【6】析构方法(__destruct())

       与构造方法对应的就是析构方法,PHP 5 同样引入了析构方法的概念,这类似于其它面向对象的语言,如 C++。析构方法会在到某个对象的所有引用都被删除或者当对象被显式销毁时执行。声明的话用__destruct()

class Person{
    public function __construct(){
        echo 'Start Strat Start __construct ';
    }
    public function __destruct(){
        echo 'End End End __destruct ';
    }
}
$obj = new Person;
echo "<br/> do something....<br/>";
//执行完毕后会输出 
/*
Start Strat Start __construct 
do something....
End End End __destruct
*/

     上面的例子中可以看出了一些区别,析构方法与构造方法不同:第一是在被调用的机制不同,第二是构造方法可以传入参数,而析构方法则不可以传入任何参数。

      通常对象的引用会在页面运行结束时候失去。当堆内存端中的对象失去访问它的引用的时候,它就不能访问了,也就成为了垃圾对象。在PHP中有一种垃圾回收的机制,当对象不能被访问时候,就会自动启动垃圾回收的程序,收回对象在堆中占用的内存空间。而析构方法则是在垃圾回收对象前进行调用的。可以利用它做一些文件关闭,或者释放结果集等。

      例题:程序输出结果是什么?

class Person{
    public $name;
    function __construct($name){#不写修饰符,默认为public
        $this->name = $name;
    }
    function __destruct(){#对象销毁前就调用 输出name属性
        echo "GoodBye".$this->name."<br/>";
    }
}
$obj1 = new Person("ONE");
$obj1 = null;
$obj2 = new Person("TWO");
$obj3 = new Person("THREE");
/*
GoodByeONE
GoodByeTHREE
GoodByeTWO
*/

        上面的程序中,类Person中的最后声明了一个析构方法__destruct(),一旦失去对象引用,这个对象就成为垃圾,从而被垃圾回收机制收回对象占用空间,在被收回的前,就调用了这个析构方法。由于第一个new的对象obj1在被new后直接就置为null,所以第一个对象先被回收然后调用析构方法。只有两个对象,由于对象的引用都是存放在栈内存中,根据栈的先进后出的特点,最后创建的对象最先被释放。

【7】封装性-关键词(public,protected,private)

      面向对象三大特征之一,把对象的全部成员属性,和全部方法结合在一起,形成一个不可分割的独立单位对象。信息隐蔽,尽可能对属性进行封装,而只保留一个对外部的接口使之发生联系。

      对象中的成员属性如果没有被封装,一旦对象完成创建,就可以通过对象的引用获取任意的成员属性的值,而且可以进行修改,这是非常危险的,就比如让别人知道你有多少钱,还可以随意花你的钱一样。

     对象中的成员方法如果没有被封装,那么对象的外部也可以随意调用其中的方法,这也不是特别合适。对象中有些成员方法只能自己进行调用,只保留一些方法供外部使用。

      封装的原则就是要求对象以外的部分不能随意获取对象的内部数据(成员属性以及方法),从而有效的避免了外部的随意访问调用发生错误,是软件错误能够局部化,大大减小排查错的难度。而对属性和方法进行封装就需要使用关键词public,protected,private这些进行访问控制。

class Person{
    private $name;
    public function __construct($name){
        $this->name   = $name;
    }
    public function getName(){
        echo $this->name;
    }
}
$obj = new Person("ONE");
$obj->getName();//输出ONE
//$obj->name; 报错 不能访问私有属性

【8】继承性-关键词(extends)

     PHP 的对象模型也使用了继承。继承将会影响到类与类,对象与对象之间的关系。可以用关键词extends来继承父类,父类也叫做基类或者超类,子类也可以叫做派生类。在软件开发中使用类的继承性,其软件会有开放性,可扩充性,提高了代码的可重用性。但是与C++不同,PHP中只能单继承,不可以一个类继承多个父类。

     比如,当扩展一个类,子类就会继承父类所有公有的和受保护的方法。除非子类覆盖了父类的方法,被继承的方法都会保留其原有功能。继承对于功能的设计和抽象是非常有用的,而且对于类似的对象增加新功能就无须重新再写这些公用的功能。

class Person{#定义人类 属性:姓名 一个构造方法传入姓名
    public $name;
    function __construct($name){
        $this->name = $name;
    }
}
class Student extends Person{#学生类继承 人类
    public $sno;#定义一个属性sno
}
$tacks = new Student('TACKS');#实例化学生 
$tacks->sno = '007';
var_dump($tacks);#可以看到学生中也有姓名的属性 同时也有学号
/*
object(Student)[1]
  public 'sno' => string '007' (length=3)
  public 'name' => string 'TACKS' (length=5)
*/

【9】访问控制-关键词(public,protected,private)

      对属性或方法的访问控制,是通过在前面添加关键字 public(公有)protected(受保护)或 private(私有)来实现的。被定义为公有的类成员可以在任何地方被访问。被定义为受保护的类成员则可以被其自身以及其子类和父类访问。被定义为私有的类成员则只能被其定义所在的类访问。也就是只有public方法可以在类的外部调用。

     类的属性前面必须加关键词,定义为公有,受保护,私有之一,如果用var定义默认视为公有。类的方法前面如果不加关键词,默认是公有。另外构造方法不要定义私有,这样会创建对象的时候报错。

  • 公有的访问修饰符public,对访问类中的成员没有限制,而且外部成员也可以访问这个类中的成员,以及子类也可以访问属性或者方法。
class Common{
    public    $var1 = 'public';
    public function fun1(){
        echo 'public fun1','<br/>';
    }
}
class MyClass extends Common{
    public function get(){#子类的方法
        echo $this->var1,'<br/>';##子类 可以直接 获取父类的属性
        $this->fun1();#子类 可以直接 调用父类的方法
    }
}
$obj = new MyClass;
echo $obj->var1,'<br/>';//外部可以直接访问到父类的属性
$obj->get();//外部可以直接访问父类的方法
echo '<hr>';
$obj->var1 = 'AAA';//修改父类的属性值
$obj->get();//可以获得修改后的值
  • 受保护的访问修饰符protected,对于父类,外部不能获取其属性或者方法,只能在内部使用。对于该类的子类内部具有访问权限,但是外部不能进行访问。这样可以实现对对象的封装。
class Father{
    protected $money = 888;
    protected function say(){
        echo 'Father say','<br/>';
    }
    public function getMoney(){
        echo $this->money;
    }
}
class Son extends Father{
    public function get(){
        echo $this->money,'<br/>';
        $this->say();
    }
}
$fa  = new Father;
//echo $fa->money,'<br/>';//父类 外部也不能访问 其属性
//echo $fa->say();//父类 外部也不能访问 其方法
echo $fa->getMoney();//父类 可以写一个public方法接口 来获取其受保护的属性
echo '<hr/>';
$obj = new Son;
//$obj->money;//子类 外部不能获取父类属性
//$obj->say();//子类 外部不能获取父类方法
$obj->get();//通过子类方法 获取父类属性以及方法
  • 私有的访问修饰符private,这种的范围仅限于在父类的内部进行使用,父类外部以及子类都访问不到。
class Father{
    private $money = 888;
    private function say(){
        echo 'Father say','<br/>';
    }
    public function getMoney(){
        echo $this->money;
    }
}
class Son extends Father{
    public function get(){
        echo $this->money,'<br/>';
        $this->say();
    }
}

$fa  = new Father;
//echo $fa->money,'<br/>';//父类 外部也不能访问 其属性
//echo $fa->say();//父类 外部也不能访问 其方法
echo $fa->getMoney();//父类 可以写一个public方法接口 来获取其受保护的属性
echo '<hr/>';
$obj = new Son;
//$obj->money;//子类 外部不能获取父类属性
//$obj->say();//子类 外部不能获取父类方法
//$obj->get();//子类   内部也获取不到父类属性以及方法

【10】类常量-关键词(const)

        可以把在类中始终保持不变的值定义为常量。在定义和使用常量的时候不需要使用 $ 符号。常量的值必须是一个定值,不能是变量,类属性,数学运算的结果或函数调用。例如人类一般都有两个眼睛,这个时候可以将眼睛的个数属性直接声明成常量并赋值为2。其中类外如果访问的话使用::(范围解析操作符),通过"类名::常量"来获取。

//下面这个可不要看花眼了
class Person{
    const eyes=2;#声明类常量 并赋值2
    public function set($num){//定义方法 
        $this->eyes = $num;#赋值给对象属性eyes
    }
    public function eyes(){
        echo self::eyes;#类内访问类常量
    }
}
$tacks = new Person;#实例化一个对昂
$tacks->set(1);#调用方法设置对象的属性
echo $tacks->eyes,'<br/>';#这个是访问对象的成员属性  //1
echo Person::eyes,'<br/>';#访问类静态属性的正确方式 类名::常量名//2
echo $tacks->eyes(),'<br/>';#内部访问类的静态属性 self::常量名//2

【11】关键字static

      声明类属性或方法为静态,就可以不实例化类而直接访问。静态属性不能通过一个类已实例化的对象来访问(但静态方法可以)。由于静态方法不需要通过对象即可调用,所以伪变量 $this 在静态方法中不可用。静态属性不可以由对象通过 -> 操作符来访问。

     就像其它所有的 PHP 静态变量一样,静态属性只能被初始化为文字或常量,不能使用表达式。所以可以把静态属性初始化为整数或数组,但不能初始化为另一个变量或函数返回值,也不能指向一个对象。

     如果一个类中声明了一个静态属性,那么不管这个类被实例化多少次,或者没有实例化,这个static成员总是唯一存在,在多个对象中共存。因为这个static标识的成员属于类的,它不属于任何对象。

看一下用法吧
类内:由于类的静态属性不属于对象,所以不能使用$this访问,而是使用self关键词。
	self::静态成员属性名;
	self::静态成员方法名();
类外:
	类名::静态成员属性名;
	类名::静态成员方法();
	对象->静态成员方法();
class Person{
    static $count;#定义类的静态属性 可以在类外初始化
    function __construct(){
        self::$count++;#访问自身的类的静态属性 计算类实例化次数
    }
    static function getCount(){
        echo self::$count;
    }
}
Person::$count = 0;//初始化为0
$obj1 = new Person;
$obj2 = new Person;
$obj3 = new Person;
echo Person::getCount(),'<br/>';//在类外可以用类名访问静态方法
echo $obj3->getCount();//在类外也可以通过对象来访问类中的静态方法
echo '<hr/>';
class Student extends Person{}
echo Student::$count,'<br/>';#子类 也可以获取其静态属性
echo Student::getCount(),'<br/>';#子类 也可以获取其静态方法

        类中的静态方法中通过self访问静态成员,如果是父类的可以使用parent访问。因为非静态的成员必须通过对象调用,而这个通常是$this。类外静态方法在对象不存在的时候可以直接通过类名进行调用,在对象存在的时候也可以用对象的引用进行调用。类定义的静态属性或者方法,在其子类中也能使用。

【12】关键字final

如果父类中的方法被声明为 final,则子类无法覆盖该方法。如果一个类被声明为 final,则不能被继承

class Person{
    final function say(){
        echo 'Hello World!';
    }
}
class Student extends Person{ 
    function say(){
        echo 'I am a student';
    }
}
//报错Fatal error: Cannot override final method Person::say() 
final class Person{
     function say(){
        echo 'Hello World!';
    }
}
class Student extends Person{}
//Fatal error: Class Student may not inherit from final class (Person) 

【13】关键字this,self,parent,static

     $this指向当前的对象,也就是当前实例化的类,在实例化的时候来确定指向。$this无法指向静态变量和静态方法。self指向类本身,不指向任何已经实例化的对象,主要用来调用静态方法和静态变量。parent指向父类,经常用于子类重写父类的方法,这里要记得子类方法如果重写了父类方法其访问权限不能低于父类被覆盖的方法权限。static用于定义静态成员属性或者方法,可以直接用类名方式进行调用,不用实例化对象。

class Person{
    const  head  = 1;#设置类常量
    static $eyes = 2;#设置静态成员属性
    public function get(){
        echo 'My Head num is:',self::head,'.  ';#访问自身类常量head
        echo 'My eyes num is :'.self::$eyes.'<br/>';#访问自身类的静态属性eyes
    }
}
class Student extends Person{
    public $sno;
    public function __construct($sno){
        $this->sno = $sno;#当前实例化对象时候,对象的属性sno被构造函数初始化。
    }
    public function get(){#子类重写父类的方法
        parent::get();#子类继承父类的方法
        echo 'This is my sno:'.$this->sno.'<br/>';
    }
    static public  function study(){
        echo 'study study study!<br/>';
    }
}
$tacks = new Student('007');//My Head num is:1. My eyes num is :2
$tacks->get();//This is my sno:007
echo Person::head,'<br/>';#获取类常量//1
echo Student::head,'<br/>';#获取类常量//1
echo Student::study();#调用类的静态方法//study study study!

【14】关键字instanceof

使用instanceof关键字可以判断一个对象是都是某个类的实例,类的子类,或者是某个特定的接口。实际上这个关键字算是php的类型运算符。

class Person{}
class Student extends Person{}
$obj1 = new Person;
$obj2 = new Student;
echo ($obj1 instanceof Person)?'Yes':'No';
echo ($obj2 instanceof Person)?'Yes':'No';

【15】抽象类-关键词(abstract)

     PHP 5 支持抽象类和抽象方法。一个类被定义为抽象的,那么它不可以被实例化,除非子类继承它并且实现它所有的抽象方法。定义抽象类相当于开发规范,这种规范子类必须去遵守实现,才可以进行实例化

     抽象方法,也就是没有方法体的方法,即方法没有大括号去实现里面的内容,声明抽象方法用关键字修饰符abstract。抽象类,一个类中如果有一个抽象方法,那么这个类就是抽象类,抽象类也要使用关键字修饰符abstract,当然抽象类中也可以有不抽象的成员属性或者方法,但是其访问权限不能用private关键词修饰。

abstract class Ab{
    #定义抽象方法,强制子类必须实现
    abstract protected function fun1();
    abstract public function fun2($value);
    #普通方法 由于fun1是protected所以要有一个接口可以调用
    function fun3(){
        $this->fun1();
    }
}
class Concrete extends Ab{
    #子类实现父类的抽象方法 fun1
    protected function fun1(){echo 'protected fun1();<br/>';}
    #子类实现父类的抽象方法 fun2
    public function fun2($value){
        echo 'fun2(param);',"{param:{$value}}",'<br/>';
    } 
}
$obj = new Concrete;
$obj->fun3();#通过调用父类的fun3来受保护的fun1   
$obj->fun2('FUN2');//实现抽象方法fun2
//protected fun1();
//fun2(param);{param:FUN2}

【16】接口interface与实现接口implements

       使用接口(interface),可以指定某个类必须实现哪些方法,但不需要定义这些方法的具体内容。它是一种特殊的抽象类,而抽象类又是一种特殊的类,所以接口也算是特殊的类。如果一个抽象类中所有的方法都是抽象方法,那么我们也可以用接口去进行声明,而且因为都是抽象方法,也就不用写abstract。而是是通过interface关键字来定义的接口类。

      接口中声明的方法必须都是抽象方法,并且不能在接口中声明变量,只能用const关键字来成名类常量但是不能被覆盖,另外接口中的所有成员方法都必须是public的访问权限。接口也可以继承使用extends。实现多个接口的时候方法不能重名。接口的实现是通过implements关键词。一个类只能继承一个类,但是可以实现多个接口,不过先写extends 后写 implements。

interface Factory{#定义接口
    const theme = 'USB';#接口可以定义类常量
    function product1();#默认就是public
}
interface FactoryA extends Factory{#接口继承
    public function product2($value);#接口的方法也只能是public
}
abstract class FactoryB implements FactoryA{#抽象类 实现其中一部分抽象方法  
    public function product2($value){echo '###',$value,'###','<br/>';}
}
class FactoryC extends FactoryB{#抽象类的子类必须实现所有抽象方法
    function product1(){echo 'Product1 ',self::theme,'<br/>';}
}
$obj = new FactoryC;
$obj->product1();
$obj->product2('Type-C');
$obj->product2('Micro');
echo ($obj instanceof Factory)?'Yes':'No';#判断一下类型
/*
Product1 USB
###Type-C###
###Micro###
Yes
*/

【17】多态性

       面向对象特性中除封装和继承之外的另一个重要特性,多态最直接的定义就是让具有继承关系的不同类对象,可以对相同名称的成员函数进行调用,产生不同的反应效果。在PHP中多特性体现在方法的重写。重写就是指一个子类中可以重新修改父类中的某些方法,使其具有自己的特征。重写要求子类的方法和父类的方法名称相同,这个可以通过声明抽象类或者接口来规范。另外如果相对父类的方法进行重写,但又想用父类的方法可以用parent关键词。

interface FruitFactory{#定义一个水果工厂 接口
    function produce();#必须实现生产的方法
}
class AppleFactory implements FruitFactory{#实现一个苹果工厂 
    function produce(){echo 'Apple','<br/>';}
}
class BananaFactory implements FruitFactory{#实现一个香蕉工厂 
    function produce(){echo 'Banana','<br/>';}
}
class Store{#定义一个商店
    function sell($fruit){#定义一个零售水果
        $fruit->produce();
    }
}
$obj = new Store;#实例化商店 
$obj->sell( new AppleFactory);//卖苹果
$obj->sell( new BananaFactory);//卖香蕉
#如果还想卖其他水果,只需要写一个类实现水果工厂的接口,然后调用商店的售卖方法就好了,什么水果都可以卖

【18】Trait

      PHP5.4后,Trait 是为类似 PHP 的单继承语言而准备的一种代码复用机制。Trait 为了减少单继承语言的限制,使开发人员能够自由地在不同层次结构内独立的类中复用 method。Trait 和 Class 组合的语义定义了一种减少复杂性的方式,避免传统多继承和 Mixin 类相关典型问题。

      声明类的时候我们用class,那么声明trait当然就用关键词trait。但是他无法自身实例化,必须混入到类中,利用use去使用。另外类有的特性trait都有,也支持final,static,abstract等这些修饰符。

trait CommonTrait{#定义一个trait
    protected $var1='CommonTrait var1';#trait的受保护的成员属性
    static $var2=1;#trait的静态属性
    public function fun1(){echo 'fun<br/>';}#trait的成员方法
    abstract public function fun2();#trait的抽象方法
}
class Demo{#由于 trait不能进行实例化,可以用类里面使用trait
    use CommonTrait;#使用trait
    public function fun2(){#由于trait里面有个抽象方法,所以要实现它
        echo 'Abstract fun2(); get ',$this->var1,'<br/>';#获取自身的var1也就是trait的成员属性
    }
}
$obj = new Demo;#实例化
$obj->fun1();#调用trait的方法
$obj->fun2();#调用类里面实现上面的接口方法
echo CommonTrait::$var2,'<br/>';#用trait名获取静态成员属性
echo Demo::$var2,'<br/>';#用包含的类来获取静态成员属性
  • 从基类继承的成员会被 trait 插入的成员所覆盖。优先顺序是来自当前类的成员覆盖了 trait 的方法,而 trait 则覆盖了被继承的方法。
trait Person{
    public function say(){
        parent::say();#这里用了一个指向 父类的。相当于重写父类的方法
        echo ' trait Person ';#然后再去实现自己的,
    }
}
class Base{
    public function say(){
        echo ' Class Base ';
    }
}
class Student extends Base{
    use Person;#这里使用trait,那么必须有父类来去完成这个say中的parent
}
$obj = new Student;
$obj->say();
  • 通过逗号分隔,在 use 声明列出多个 trait,可以都插入到一个类中。如果两个 trait 都插入了一个同名的方法,如果没有明确解决冲突将会产生一个致命错误。为了解决多个 trait 在同一个类中的命名冲突,需要使用 insteadof操作符来明确指定使用冲突方法中的哪一个。
//使用多个trait
trait A{
    public function say(){echo 'A','<br/>';}
}
trait B{
    public function say(){echo 'B','<br/>';}
}
class letter{
    use A,B{
        A::say insteadof B;//如果冲突了say就使用A的方法。
    }
}
$obj = new letter;
$obj->say();

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值