乐趣区

关于php:访问控制与继承

访问控制与继承

[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 小毛驴
退出移动版