对象的继承特性

一、对象的继承

  1)什么是继承?

    父类的内容可以拿到子类当中来使用

  2)为什么要使用继承?

    ①更好地体现面向对象的可重用性

    ②避免代码的冗余

    ③可以在父类的基础上进行功能的扩展

    ④体现了面向对象的可扩展性

  3)什么时候使用继承?

    ①在功能要进行扩展的时候进行继承

    ②在功能要进行修改的时候,子类的功能会覆盖父类的功能

  4)PHP继承的特性:

    ①只支持单一继承,不支持多重继承

    ②一个子类只允许有一个父类

    ③一个父类可以有多个子类

  5)PHP继承的关键字

    ①父类、基类、超类

    ②子类、扩展类、派生类

  6)权限问题

    ①public:公有的成员在子类中可以被完全继承;子类中的成员如果与父类的公有成员的名称相同,则子类的成员功能将覆盖父类的成员功能;

    ②protected:受保护的成员在子类当中可以被完全继承;受保护的成员在子类中如果出现同名的成员,功能会被覆盖;

    ③private:父类中私有的成员在子类中不能直接拿来使用,不可被继承

    ④private < protected < public,子类中的同名的成员的权限一定要比父类的成员的权限宽松

  7)方法的重载(覆盖)

    parent::__construct();

    parent::成员方法名();

class Person{
    public $name;
    public $sex;
    public $age;

    function __construct($name,$sex,$age){
        $this->name=$name;
        $this->sex=$sex;
        $this->age=$age;
    }

    public function say(){
        echo "my name is ".$this->name.", my sex is ".$this->sex.", my age is ".$this->age;
    }
}

class Student extends Person{
    //继承成员属性$name、$sex、$age
    public $school;

    //构造方法同样被继承,并被覆盖重写
    function __construct($name,$sex,$age,$school){
        //将父类的构造方法的方法体继承下来
        parent::__construct($name,$sex,$age);

        $this->school=$school;
    }

    //将父类的say方法覆盖重写
    public function say(){
        //将父类的say方法的方法体继承下来
        parent::say();

        echo ", my shcool is ".$this->school;
    }
}

$p=new Student("autumn","男",25,"haha");
$p->say();

二、常用的关键字

  1)final

    ①final修饰的成员方法不允许在子类当中被重写,为了防止已经写好的功能被修改

    ②final只能修饰方法和类,不能修饰成员属性

    ③final修饰的类不能再被其他类继承

  2)static

    ①static修饰的成员属性,在类的方法体中要使用“self::$属性名称”访问

    ②static修饰的成员属性保存在内存中的初始化静态段中,而在类中使用“$this->属性”所调用的成员属性是保存在堆当中的,所以static修饰的成员属性不能使用“$this->属性名称”来访问

    ③静态成员属性在类的外部可通过“类名::$成员属性名称”调用,不可使用对象来调用

    ④静态成员属性效率较高,因为不需要实例化对象即可调用

    ⑤同一个类的多个实例化对象共享一个静态成员属性

    ⑥静态的成员方法中不允许使用$this调用成员属性

    ⑦静态的成员方法在类的内部可以使用$this调用,也可以使用“self::方法名()”调用

    ⑧静态的成员方法在类的外部可以使用实例化对象调用,也可以使用“类名::方法名()”调用

    ⑨成员方法当中如果没有包含任何$this,该方法默认为静态成员方法

class Person{
    public $username;
    public static $name;

    public function info(){
        //普通成员属性的调用
        //echo $this->username;

        //静态成员属性的调用
        echo self::$name;
    }   

    public static function say(){
        echo self::$name." say 'hello world!'";
    } 

    public function test(){
        //可以使用$this调用静态成员方法
        //$this->say();

        //也可使用“self::方法名()”调用
        self::say();
    }
}

//静态成员属性不可以使用对象来调用
$p1=new Person;
Person::$name="autumn";

$p2=new Person;
Person::$name="HaHa";

//多个实例化对象共享一个静态成员属性

$p1->info();
$p2->info();

echo "<hr>";

//可以用实例化对象调用静态成员方法
//$p1->say();
//也可以使用“类名::方法名()”调用(推荐使用)
Person::say();

  3)单态设计模式

    ①什么是单态(单例)(单对象)设计模式?

      让类只能实例化一个对象

    ②为什么要使用单态?

      为了节省空间、提高效率

    ③什么时候使用单态设计模式?

      数据库的连接池操作、文件资源的操作、GD资源的操作

    ④如何使用单态设计模式?

      1.构造方法私有化,外部无法自动实例化对象

      2.声明一个方法,在该方法中实例化对象

      3.将产生的对象存入至一个共享的静态成员属性中

      4.在外部想要再次实例化对象时,将该静态成员属性中的对象传给它,即仍为首次实例化的那个对象

class Person{
    public static $obj;  //用于保存首次实例化的对象
    public $name;

    //构造方法私有化,外部无法自动实例化对象
    private function __construct($name){
        $this->name=$name;
    }

    public static function getObj($name){
        //判断该类的对象是否存在
        if(is_null(self::$obj)){
            self::$obj=new self($name);
        }
        //除首次实例化对象外,其余直接将首次实例化后存在静态成员属性中的对象传递过去
        return self::$obj;
    }  

    public function info(){
        echo $this->name;
    }
}


$p1=Person::getObj("autumn");
var_dump($p1);

echo "<hr>";

$p2=Person::getObj("haha");
var_dump($p2);

object(Person)#1 (1) { ["name"]=> string(6) "autumn" }


object(Person)#1 (1) { ["name"]=> string(6) "autumn" }

  4)const

    ①常量名一般情况下用大写

    ②常量值为标量数据类型

    ③常量不要加“$”

    ④常量在类的内部访问用“self::常量名”

    ⑤常量在类的外部访问用“类名::常量名”

  5)instanceof

    ①判断一个对象是否为一个类实例化的

    ②判断一个对象是否为继承父类的某个子类实例化的

class Person{
    //声明一个常量成员属性
    const DB_HOST="localhost";

    public function info(){
        //类的内部调用常量成员属性
        echo self::DB_HOST;
    }   
}

class Student extends Person{
    public $school="hehe";

    public function say(){
        parent::info();
        echo ", my school is ".$this->school;
    }
}

//类的外部调用常量成员属性
echo Person::DB_HOST;

echo "<hr>";

$p=new Person;
$s=new Student;

//instanceof用于检测当前实例对象是否属于某一个类
var_dump($p instanceof Student);  //false
var_dump($s instanceof Student);  //true
var_dump($s instanceof Person);  //true

三、常用的魔术方法

  1)克隆对象

    ①什么是克隆?

      将一个对象完整的复制一份,这个对象与原对象一模一样

    ②克隆的目的

      当对一个已经产生的对象进行一系列的修改、赋值等操作后,需要使用多个经操作后的对象时,使用克隆

    ③方法:__clone()

      参数:无

      触发时机:在使用clone对象的一瞬间自动调用

      作用:将克隆出的对象进行微调,源对象不会被影响

  2)__toString()

    参数:无

    触发时机:当在类的外部直接echo对象时,自动调用

    作用:为了更好地操作对象

    注意:本方法一定要有返回值

  3)__call()

    参数:两个(第一个是调用方法的名称,第二个是实际参数的数组)

    触发时机:当调用一个不存在的成员方法时自动调用,且将方法的名称传递给第一个参数,将方法的调用参数传递给第二个参数

    作用:避免程序报错,影响后续代码执行

class Person{
    private $name="autumn";
    private $sex="男";
    private $age=25;

    public function info(){
        echo "姓名:".$this->name.",性别:".$this->sex.",年龄:".$this->age;
    }

    //在使用clone对象的瞬间自动调用__clone()方法
    function __clone(){
        $this->name=$this->name."_clone001";
    }

    //在类的外部echo对象时自动调用__toString()方法
    function __toString(){
        return "在类的外部直接echo对象会输出该段文字";
    }

    //当调用一个不存在的成员方法时自动调用__call()方法
    function __call($funcName,$args){
        echo "对不起,您要找的方法".$funcName."(".join(',',$args).")不存在!";
    }
}

$p1=new Person;

//将对象$p1完整克隆一份
$p2=clone $p1;

var_dump($p1);
echo "<hr>";
var_dump($p2);

echo "<hr>";
echo $p1;

echo "<hr>";
$p1->noFunc('参数1','参数2','参数3');

  4)__sleep()

    参数:无

    触发时机:当对一个对象序列化的时候自动调用

    作用:让用户可以自定义要序列化的成员属性

    注意:该方法一定要返回一个数组类型的值,数组元素的值即为成员属性的名称

  5)__wakeup()

    参数:无

    触发时机:当对对象进行反序列化的时候自动调用

    作用:反序列化对象时使对象中的成员发生改变

  6)对象串行化(序列化)

    将对象转化成以固定形式存储的字符串

    ①为什么使用对象的串行化?

      为了能够让保存对象的介质能够识别对象的格式

    ②何时使用对象的串行化?

      要将对象存入到数据库或是文件中

      对象在网络中传输的时候可串行化

    ③方法:

      serialize($obj):串行化(序列化)

      unserialize($str):反串行化(反序列化),将对象反串行化时必须有原型类同时在

class Person{
    private $name="autumn";
    private $sex="男";
    private $age=25;

    public function info(){
        echo "姓名:".$this->name.",性别:".$this->sex.",年龄:".$this->age;
    }

    public function __sleep(){
        //指定要序列化的成员属性
        return array('name','sex');
    }
}

$p=new Person;

echo $str=serialize($p);

file_put_contents("./obj.txt",$str);
class Person{
    private $name="autumn";
    private $sex="男";
    private $age=25;

    public function info(){
        echo "姓名:".$this->name.",性别:".$this->sex.",年龄:".$this->age;
    }

    public function __sleep(){
        //指定要序列化的成员属性
        return array('name','sex');
    }

    public function __wakeup(){
        $this->name="admin";
        $this->sex="保密";
    }

    public function say(){
        echo "haha";
    }
}

$str=file_get_contents("./obj.txt");

//将对象反序列化时,必须要原对象类同时在
$obj=unserialize($str);

$obj->info();

  7)__autoload()

    参数:一个(类的名称)

    触发时机:当要实例化或继承使用未经include引入的类文件中的类的时候会自动调用该函数,且会将类名作为参数传入该函数,此时在该函数内部可实现类文件的加载

    作用:用于加载类文件,无需像使用include加载类文件那样考虑加载的先后顺序

    注意:这是一个函数并非是类的成员方法,不是用于类的内部的,而是在需要引用类文件时使用的

function __autoload($className){
    if($className=='class1'){
        include "../".$className.".class.php";
    }elseif($className=='class2'){
        include "../".$className.".class.php";
    }elseif($className=='class3'){
        include "./".$className.".class.php";
    }
}

$p=new Person;

四、类型约束

  1)类型约束可以使用数组约束和类名约束

  2)数组约束:只需要在方法或函数的参数前加入array关键字即可

  3)类名约束:只需要在方法或函数的参数前加入类名,表示调用时必须传递该类的对象或该类的子类的对象

class Person1{
    function say(array $info){  //数组约束
        echo "P1:".join(',',$info);
    }
}

class Person2{
    function say(array $info){  //数组约束
        echo "P2:".join(',',$info);
    }
}

class Person{
    function test(Person1 $obj){  //类名约束
        $obj->say(array('abc','ABC'));
    }
}

$p=new Person;
$p->test(new Person1);

 

posted @ 2018-09-04 21:56  Autumn_n  阅读(538)  评论(0编辑  收藏  举报
TOP