开发者学堂课程【PHP 进阶教程-由浅入深掌握面向对象开发-第二阶段:继承相关概念和实现】学习笔记,与课程紧密联系,让用户快速学习知识。
课程地址:https://developer.aliyun.com/learning/course/712/detail/12692
PHP 继承——有限继承
内容介绍:
一、目标
二、概念
三、示例
四、小结
前面的课讲了继承语法,发现继承之后,子类对象可以访问父类的成员,问题是哪些能够访问?哪些用来被继承?有没有不可被继承的?所以本节,通过有限继承来学习解决这些问题。
一、目标
本节目标:理解 PHP 中继承的内容(继承哪些东西),并且掌握架构中的设计方案,去实现真正的应用继承。
二、概念
1.有限继承:指子类在继承父类成员的时候,并非继承所有内容,而是继承并使用父类部分内容(只能使用其一个部分的内容)。
(1)PHP 中继承的本质是对象继承。(对象去访问的是属性和方法,所以继承通常是指对象继承。)
(2)PHP 中继承的内容:父类所有公有成员(属性和方法,都可以继承)受保护成员和私有属性都可以继承,唯独私有方法不能继承。这点需要明确。
(3)受保护(protected:关键字,访问修饰限定符号)成员是专门用于实现继承的,专门为继承而生,可以在父类或者子类的内部访问(相关类的内部),这时就体现出了相关性:“子类和父类都可以访问。”这叫做相关类,但前提是要实现继承,当然不继承也无法体现叫父类或者子类。
(4)私有成员的访问,虽然属性被继承,但是只能在所属类(因为私有成员只能在自己的类里进行访问)中设定公有或者受保护的方法来实现访问。不然子类里面也访问不了私有属性,因为私有的特性是只能在自己类里面的方法进行访问。
(5)为了使结构的完整性得到保留,静态成员也遵循继承规则:因为静态成员所属于类,从结构的角度分析,类继承类,所以也可以享受这种待遇,即子类可以访问父类静态成员。(前提是满足继承条件,就可以实现访问)
这是所谓有效继承的概念,指继承所有的部分,只有私有方法不能被继承,其他的都可以被继承。
三、示例
有了这种继承方式,接下来看具体的实现
1. 继承内容: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() ; #允许直接访问:方法为公有允许类外访问
关键观察对象里有什么能帮助此处得到继承,新建02extens.php 这样的一个有限继承,因为此类代码较多,但本身比较简单,故将其复制过来。
同时具有父类、常量、属性、方法。属性以及方法都有范围修饰限定符限定,公有以及私有都具有。然后子类继承父类继承,输入 class Man extends Human{}这样就完成了继承。完成继承之后,接下来输入空类(因为没进行其他操作)。
然后进行实例化(子类),实例化父类与子类没有关系,输入$m = new Man() ;var_ dump($m) ;
然后可以观察对象到底属于什么类型。因为如果其私有属性可以继承,那本身的对象(Man)对象没有属性,除非其私有在这里存在,才可以证明真的继承成功了。
访问02extens.php,发现里面确实拥有三个属性。不管是 man 这样类的对象、公有的 name 属性,还是受保护的 age 属性,又或者是 private 的 human 类的 money 属性全部都有。说明属性都被继承了,那属性继承了,当然要确定所谓的 showmoney 能否访问私有方法,私有的 showname 和公有的肯定可以进行访问。showage 受保护的,私有的则是不一定能访问。那么先进行属性证明,先证明父类属性(父类本质输入的是父类对象)确实被子类继承了。
接下来看方法的调用,调用 showname,输入“$m->showname();”这是公有方法,点击刷新,结果显示成功。再输入“$m->showage();”点击刷新,结果显示错误,错误的原因是访问了一个受保护的方法,但是环境不对。
出现这种错误的原因是受保护的只能在类的内部访问,但是是相关类。
2. 受保护继承 protected,protected 的关键字的产生本身就是纯用于继承的,表示允许被子类在子类内部访问的意思,而不允许被外部直接访问。
其只能证明,在类的内部访问。如果需要在外部访问,则不能证明其可以被继承或者使用。所以需要在子类里面增加一个方法来访问父类受保护的内容,包括属性和方法。
#父类
class Human{
protected $age =‘100’;
protected function showage(){
echo Sthis->age;
}
}
#子类
class Man extends Human{
#在子类内部增加公有访问访问继承自父类的受保护成员
public function getprotected( ){
echo sthis->age; #访问父类受保护属性
$this-> showAge(); #访问父类受保护方法
}
}
#实例化子类对象
$m = new Man();
$m->getProtected(); #正确输出:说明可以访问
子类通常是有内容的,在子类的{}中输入“Public function getprotected(){”。
第一步,输入# 访问继承的受保护内容,然后输入 echo $this->age(受保护的有 age),然后再调用父类的方法 Showage,也就是输入$this-> showAge();这时候看这个情况到底能否成功,在外部需要让此方法触发,shouage 既然不能访问,那这里就访问自己的方法(getprotected),因为这是公有的,所有一定可以访问。点击刷新发现,不管是访问 showage,还是直接访问 age 都输出了100,这也说明说明受保护的内容不管方法还是其他,确实可以被继承。
接下来需要证明私有。私有按照这种方式,输入“Public function getprivate”,然后输入 echo $this->money,很明显这里明显不能进行访问。接着输入$this->showMoney();
让此方法运行一下,下方新建一行 getPrivate(),点击刷新发现结果错误,错误原因第一个是 Man 里没有叫做$money 的属性,它并没有显示不能访问,其只是告诉当前 Man 类里没有叫做$money 的属性。这里是继承了,但是继承对应的 money 属性,但是 money 是私有的。私有的其前面是带着类名的(Human),所以其实这里确实没有属于当前 man 类能访问的 money 属性。
那此方法很明显也是没有访问的,私有的方法不允许进行访问。说明私有的方法,无法实现访问父类的内容的任务。还有一点是属性也无法访问,虽然可以继承,但不代表一定可以访问。所以这个地方可以证明:被继承,但是无法在子类中访问,这是私有的特性。虽然下面也无法证明没有被继承,但是一定是不能访问的。实际确实没有被继承,这就是上文的私有的内容。
3. 访问父类私有成员:子类若想访问父类私有成员,那么前提是父类提供了能够访问私有成员的接口:即提供了公有或者受保护的方法给子类访问。
私有成员,如果想要去访问父类的私有成员,前提就是除非父类开放接口允许访问。不管使用了受保护还是 public,其开放了接口才能去访问,如果没有开放则不能访问。如果父类允许私有给你访问的情况,其通常会进行一个受保护,因为其不允许外部访问,但是允许子类访问(受保护专为子类提供)。
#父类
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,表示正确访问
所以如果确实要进行这样的操作,可以这样进行:输入 protected function getmoney(){ ,然后输入 echo $this->money。上面是可以进行访问的,因为上面在父类里。在下面需要调用时,这两个错误的不能调用,但如果非要用父类的公共接口是没有问题的。父类私有属性,除非父类提供能继承的方法访问,不然无法进行访问。这时的方法是输入$this->getMoney();这个结果的值100是可以数出来的,而且不会报错,以上介绍了继承内容,包括继承之后怎么去使用,protected 关键字是用来修饰什么样的内容,修饰之后的效果都在这里体现。
注意:虽然子类可以通过以上方式来实现访问父类的私有成员(包括私有方法),但是从设计的意义上讲,私有就是不允许外部访问,所以父类通常不会提供对外的访问接口,以上案例只是为了知识说明。虽然能够允许通过以上方式修改父类去实现目标,但正常情况从设计的意义来讲,做私有的目的就是为了不允许外部访问,只能在内部访问,所以也没有必要为子类对象去开放私有的访问。如果可以外部访问就直接设计成 protected 这样的特性,所以上述情况,基本上用的也并不是很多。
4. 静态成员(类常量)也遵循继承规则(PHP 继承本质是对象),只是访问方式是由类进行访问。
上面都是以对象为例子来讲的,里面没有涉及到类的访问,但是本质上来讲,静态的成员也是遵循继承的规则,只是访问方式不再是由对象来访问,而是由类来访问。
例如 Man、$count,首先访问子类,子类没有的情况下找父类,父类拥有就可以访问,方法同样也可以访问。如果有受保护的静态方法想要被访问,使用在子类里面增加公共方法以及共有的方法去将其调用,这里使用类名去进行一些相应的访问,因为这是对应的范围解析操作符来操作,是类成员来访问。可以自行去运行一遍这个代码,因为本质与对象的继承实现是一样的。
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 Humanf
#依然需要子类中访问受保护成员
pub1ic static function getHuman(){
Human: :getType();
}
}
echo Man: :Scount; #允许直接访问
Man: :getcount() ; #访问父类静态方法
Man: :getHuman() ; #利用子类公有方法访问父类受保护成员
5.构造方法和折构方法也可以被子类继承,此时需要注意子类对象实例化时对应的父亲扩造方法的参数
#父类
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); #正确
会被子类继承这点要特别注意,因为有时父类有构造方法,然后发现子类也需要构造方法,或者发现子类就没有,去实例化时忽略了要去提供数据等。例如在上面有 money,但是给其增加一个构造方法,以构造方法为例,因为析构方法本身用的不多。这里增加了一个要属性的父类构造方法,然后此时这段代码本来正常运行,但是现在一运行就会报错。就是因为此类虽然自己本身没有构造方法,但是继承了父类构造方法,它需要一个参数,所以在 man()必须要给它传个值,例如五块钱。
传之后这段代码又可以运行,所以这是构造方法特性,构造方法通常都是公有,所以都会被继承。此时如果涉及到还有要继承的情况,注意父类如果私有化构造方法,那么子类是否会受到影响呢?输入Private 观察,对比一下发现子类依然显示是错误,说明了私有的方法确实是不能访问。这里其实也能说明,父类的私有方法其实本质也是被继承的,只是不能用而已,所以继承就可以理解为继大承,即所有东西都被继承。所以这种方法要做的特殊一点,构造方法要特殊。并且要注意,因为此是无法控制的,其一定会被调用,析构方法也一致,出现这种情况也会被调用。只是析构并不重要,所以一般不存在问题。
四、小结
1、继承是有限继承,理论上是用来继承父类允许被继承的部分,实际上都可以继承,只是继承完无法使用,即使用 public 或者protected 修饰的成员 (有限继承指的是有限能用而已)。
例如私有的属性和方法可以继承,但无法使用。
2、因为对象的属性是保存在对象内存空间,所以父类的私有属性也会继承。
继承的本质就是把两个类里所有的东西都拿过来,这才有了前面打印子类对象时发现父类所以属性都在其中。
3、父类私有成员本质不允许被子类访问,但是可以通过父类开放接口实现(一般不会这么操作)
4、静态成员也可以遵循继承规则。
可以独自运行上文提供的代码,去理解怎么实现继承,继承之后不再用对象而是用类访问。
5.构造方法也可以被继承,因此在实例化子类对象的时候,要考虑到父类构造方法所使用到的参数问题。
在实例化当中,在父类里面没有特殊情况,一般不会让父类使用构造方法,或者除非子类不用构造方法,一般就会使用父类的统一构造方法,父类有构造方法的前提下,在实例化子类对象的时候要考虑传参的问题,保证父类的对象可以真正的被实例化从而被子类继承。