访问控制与继承
[TOC]
本文验证剖析失去的论断
- 类内调用,不受访问控制束缚限度。类间调用,则受访问控制束缚限度。
- 只有 public 润饰的成员,能力在类外被拜访。即实例化对象只能够间接拜访被 public 润饰的成员。(留神:当类中申明魔术办法 __get (), 实例化对象拜访类未定义或公有的成员是间接拜访。)
-
子类与父类中同时定义一个同名的办法。
- 父类中的同名办法被 private 润饰,子类中的同名办法与父类的同名办法,不形成重写(overriding)关系。并且①如果父类中有其余私有办法调用此同名办法②子类不重写调用此同名办法的私有办法,那么子类的实例化对象拜访这个私有办法,私有办法外面调用的还是父类的同名办法。(其余文章参考:从父类继承的函数中的 $ this 依然指向 php 中的父对象?)
- 父类中的同名办法被 public 或 protected 润饰,子类中的同名办法与父类的同名办法,形成重写(overriding)关系,但要求 润饰子类的同名办法的拜访修饰符的作用范畴 >= 润饰子类的同名办法的拜访修饰符的作用范畴,否则报致命谬误。个别代码编辑器也会提醒。
- $this 是一个伪变量,示意对主叫对象的援用。当一个办法在类定义外部被调用时,可应用 $this。
__CLASS__
魔术常量,示意以后类。作用与 get_class() 雷同,要想获取主叫对象的所属类的类名,可应用 get_called_class() 函数,或者 get_class($this)。
一、访问控制
类中的成员(属性或类常量或办法)前无任何拜访修饰符润饰时,默认被 pubic 润饰。
访问控制修饰符 | 作用范畴 | 是否参加继承 |
---|---|---|
public | 以后类外部、子类外部、以后类内部、子类内部 | YES |
protected | 以后类外部、子类外部 | YES |
private | 只能在以后类外部被拜访 | NO |
留神:
只有参加继承(被 public 或 protected 润饰)的成员办法,才能够 overriding(重写)。因为 private 润饰的公有办法对于子类来说,是不可见的,即 *** 简直能够认为公有办法不参加继承 ***。此外,php 中的(overload)重载,并不是真正意义上的、像其余语言(如 java)所形容的那种概念。
PHP 重载。
示例代码
上面的代码写在名为 base.php 的文件中,运行环境 PHP 8。
<?php
class Super
{
public $name; // 姓名
public $gender; // 性别
protected $birth; // 出生日期
private $telephone; // 手机号码
// 构造方法
public function __construct($name, $gender, $birth, $telephone)
{
$this->name = $name;
$this->gender = $gender;
$this->birth = $birth;
$this->telephone = $telephone;
}
// 公有办法
private function printHello() {echo __CLASS__ . 'hello' . PHP_EOL;}
// 申明一个私有办法, 调用下面的公有办法 printHello
public function printTest() {$this->printHello();
}
}
class child extends Super
{
// 申明一个私有的与父类中公有办法 printHello 同名的办法 printHello, 看是否形成对的父类中的同名办法的重写
private function printHello() {echo "阿凡提 de 小毛驴";}
}
$init = ['绘梨衣', '女', '2003-08-12', '158xxxx0812'];
$super = new Super(...$init);
$child = new child(...$init);
echo $super->printTest();
echo $child->printTest();
运行下面的代码,后果输入:
[Running] php "base.php"
Super hello
Super hello
[Done] exited with code=0 in 0.086 seconds
重点剖析
将下面的代码精简一下, 重点剖析子类继承父类的私有或受爱护的办法中,调用父类的公有办法,而后又在子类中申明同名的公有办法,$this 援用的疑难
。
先论述一下我的思路:
先申明下,我理解访问控制的基本概念,晓得被 private 润饰的办法只能在本类调用。故而,我认为,当子类继承父类时,子类领有了父类全副的非公有办法,即父类全副的非公有办法都存在于子类(其实子类继承了父类的全副办法,只是父类公有办法不能被子类拜访)。基于父类公有办法不能被子类拜访,而子类有继承了父类的私有办法。因而,当父类的非公有办法中存在调用父类公有办法,且父类被继承后。我认为是会报错的,因此须要从新申明一个与父类公有办法同名的办法(这个从新申明的办法能够被任意的拜访修饰符润饰,并且咱们晓得,这并不形成重写关系)。` 而后,问题来了。子类中不须要从新申明与父类公有办法的同名办法,因为他不会报错,从新申明了这个与父类公有办法的同名办法也没用,调用继承的非公有办法,外面代码执行的还是父类的公有办法,而非在子类内从新申明的与父类公有办法同名的办法。
下面的形容可能太罗嗦了,间接看代码吧。我将把它分为四种状况去探讨。
第一种状况:子类间接继承父类,子类外部不实现任何办法。
<?php class Super { // 公有办法 printHello private function printHello() {echo get_called_class() . 'hello' . PHP_EOL; } // 申明一个私有办法, 调用下面的公有办法 printHello public function printTest() {var_dump(get_class($this)); var_dump(get_class_methods($this)); $this->printHello();} } class child extends Super { } $super = new Super(); echo $super->printTest(); echo '------------------------------------'.PHP_EOL; $child = new child(); echo $child->printTest();
输入的后果如下。
从输入后果来看,咱们晓得子类继承了父类的全副办法,包含 Super 公有的办法 printHello()
。显然这并不合乎咱们的预期,因为在其余强类型面向对象语言中,private 润饰的办法被形容为不可继承的。这里咱们先能够认为这是因为 php 底层实现导致的起因,先不予考虑,也简直认为在 php 中,公有办法不能被继承。对这里有疑难的同学可先参考这篇文章,PHP 内核摸索:继承,多态与抽象类。基于这样的思考,我认为子类中只存在一个私有办法 printTest(),在类外,Child 的实例化的对象 $child 在调用 printTest() 时,会因为 Child 中没有可拜访的 printHello() 办法,收回 Fatal Error。然而,和我意料的不统一。$child->printTest() 时,printTest() 外部的 $this->printHello() 拜访的是父类的公有办法 printHello(),且此时的 $this 是 $child 对象的援用。
这不禁让人纳闷,子类的实例化对象的援用 $this 怎么能够拜访父类的公有办法?
这不迷信。string(5) "Super" array(2) {[0] => string(10) "printHello" [1] => string(9) "printTest" } Super hello ------------------------------------ string(5) "child" array(2) {[0] => string(10) "printHello" [1] => string(9) "printTest" } child hello
第二种状况:子类申明一个与父类公有办法同名的 printHello() 办法
留神:此办法并不与父类中的 printHello() 形成重写关系。
<?php class Super { // 公有办法 printHello private function printHello() {echo get_called_class() . 'hello' . PHP_EOL; } // 申明一个私有办法, 调用下面的公有办法 printHello public function printTest() {var_dump(get_class($this)); var_dump(get_class_methods($this)); $this->printHello();} } class child extends Super {public function printHello() {echo "阿凡提 de 小毛驴";} } $super = new Super(); echo $super->printTest(); echo '------------------------------------'.PHP_EOL; $child = new child(); echo $child->printTest();
后果输入如下。从后果能够看出,$child->printTest() 时,此时的 $this 是 $child 对象的援用,
printTest() 外部的 $this->printHello() 拜访的仍然是父类的公有办法 printHello(),而非子类中申明的 printHello()
。这又让人更加纳闷了。string(5) "Super" array(2) {[0] => string(10) "printHello" [1] => string(9) "printTest" } Super hello ------------------------------------ string(5) "child" array(2) {[0] => string(10) "printHello" [1] => string(9) "printTest" } child hello
第三种状况:子类重写父类私有办法 printTest(),但函数体不变。
<?php class Super { // 公有办法 printHello private function printHello() {echo get_called_class() . 'hello' . PHP_EOL; } // 申明一个私有办法, 调用下面的公有办法 printHello public function printTest() {var_dump(get_class($this)); var_dump(get_class_methods($this)); $this->printHello();} } class child extends Super {public function printTest() {$this->printHello(); } }
后果输入如下。此时的后果是合乎预期的,$child->printTest() 是产生致命谬误。因为 Child 中并没有能够拜访的 printHello() 办法。但即便这样,比照第一种状况,也呈现了新的疑难?子类中重写的 printTest() 与 继承的 printTest() 实现上是一样的,为什么第一种代码不报错,而以后的代码会报错,是因为继承的 printTest() 和 重写的 printTest() 在内存中存储的地位不一样吗?
string(5) "Super" array(2) {[0] => string(10) "printHello" [1] => string(9) "printTest" } Super hello ------------------------------------ Fatal error: Uncaught Error: Call to private method Super::printHello() from context 'child' on line 26 Error: Call to private method Super::printHello() from context 'child'
第四种状况:子类申明一个与父类公有办法同名的 printHello() 办法,并且子类重写父类私有办法 printTest(),但函数体不变。
<?php class Super { // 公有办法 printHello private function printHello() {echo get_called_class() . 'hello' . PHP_EOL; } // 申明一个私有办法, 调用下面的公有办法 printHello public function printTest() {var_dump(get_class($this)); var_dump(get_class_methods($this)); $this->printHello();} } class child extends Super {// public function printHello() { // echo "阿凡提 de 小毛驴"; // } // 申明一个私有办法, 调用下面的公有办法 printHello public function printTest() {$this->printHello(); } } $super = new Super(); echo $super->printTest(); echo '------------------------------------'.PHP_EOL; $child = new child(); echo $child->printTest();
此时后果输入如下。我想此种状况是最没有疑难的,也是合乎预期的。
string(5) "Super" array(2) {[0] => string(10) "printHello" [1] => string(9) "printTest" } Super hello ------------------------------------ 阿凡提 de 小毛驴