php面向对象——继承

继承概念

  • 继承不能简单的理解成子类定义时,会把父类的属性声明,方法定义拷贝一份,而是建立了继承查找关系
  • 以一个类为父类,另一个类作为其子类,子类在继承了父类属性/方法的基础上还可以进一步增添或修改,提高类代码复用性
  • 继承的根本作用是解决代码的复用性,减少冗余度,同时利于类的维护和功能的扩展
  • 继承时的关键字是 extends 不能修改

权限修饰符

个人理解其实继承不用想那麽多, extends之后,子类和父类建立继承查找关系,子类对象访问一个属性/方法,先到子类找,子类有优先用子类,子类没有用父类,至于有没有权限用,根据权限修饰符判断就完事了!

非继承时

  • public 在类{ }内外都可以访问
  • protected和private只能在类{ }内访问,可以在类{ }添加公共方法在类外间接访问

继承时

  • public 父类和子类{ }内以及类{ }外都能访问
  • protected 在父类和子类{ }内可以访问,在类{ }外不能访问
  • private 只能在本类{ }内访问

继承示例

代码

class Person{
    public $hobby = '画画';
    protected $money = 1000;
    private $friend = '紫霞';

    public function getFriend(){
        // private属性/方法只能在本类{}内访问
        echo $this->friend,'<br>';
    }
}

class Stu extends Person{
    public $school = '蓝翔';

    public function getMoney(){
        // proteced属性/方法可以在本类{}和子类{}内访问
        echo $this->money,'<br>';
    }
}

$s = new Stu();
echo $s->hobby,'<br>';
// protect和privat属性/方法无法在类{}外访问
//echo $s->money;
//echo $s->friend;
$s->getFriend();

内存
在这里插入图片描述

继承的细节

子类不能直接继承多个父类,但可以多继承

  • 多继承就可以实现直接继承多个父类的效果
  • 这里仅限php,有些语言可以直接继承多个父类
class A{
    public $name = 'zy';
}
class B extends A{ 
    public $age = 18;
}

class c extends B{}
//class c extends A,B{}  这是错的

构造方法的继承(子类未重写构造函数,创建子类)

  • 构造函数的继承与普通函数的继承一样
  • 子类未重写构造函数,创建子类对象时会默认直接调用父类
  • 子类定义了构造函数,就是重写了构造函数
  • 如果需要调用父类的构造函数,可使用 parent::__construct()
class Mysql{
    protected $conn;
    public function __construct(){
        $this->conn = mysqli_connect('localhost','root','');
    }
}

class Mydb extends Mysql{
    public function __construct(){
        // 如果子类继承时重写了构造函数,保险一点
        parent::__construct();
        // 然后再加自己的业务逻辑
        echo '重写了构造函数';
    }
}

$mysql = new Mydb();

子类调用父类方法的三种方式

class A{
    public function say(){
        echo 'hello';
    }
}
class B extends A{
    public function test(){
        // 第1种:parent绑定的是父类类名,静态访问,不管子类有没有重写say()方法,只能调到父类的
        parent::say();
        // 第2种:this绑定的是调用该方法的对象,如果子类中没写say(),可以调到父类的say(),如果子类重写say(),那这种方法只能调到子类的say()
        $this->say();
        // 第3种:静态访问,相比于第1种不够灵活,若改了类名,这里就g了
        A::say();
    }
}

$b = new B();
$b->test();

重写 (override)

  • 方法的重写就是子类有一个方法,和父类(基类)中的某个方法的名称参数个数都一样,那么我们就说子类的这个方法重写了父类的那个方法
  • 属性也是可以重写的,子类中的属性名和父类一样,就是子类重写了父类的该属性
  • 子类重写父类属性/方法后,子类对象优先调用子类的属性/方法
  • 父类的私有属性/方法不能被子类重写
  • override,有人也叫覆盖,但官方文档一般称为重写,后面统一称为重写

方法重写细节

在子类内通过 parent::方法名访问父类被重写的方法

  • 这其实就是静态访问,用parent代替父类名
  • 静态访问的规则这里不罗嗦,能不能访问到依据权限修饰符判定
class A{
    public function say(){
        echo 'hello';
    }
}
class B extends A{
    public function say(){
        echo 'shit';
        parent::say();
    }
}

$b = new B();
$b->say();

方法重写时名称和参数都要一致,如果参数不一致会有警告

  • 若方法名一样,参数不一致,系统会警告,但依旧重写了父类方法
  • 如果父类的参数使用了类型约束,还必须保证数据类型一致
class A{
    public function say($num){
        echo 'hello';
    }
}
class B extends A{
    public function say(){
        echo 'shit';
    }
}

$b = new B();
//  Warning: Declaration of B::say() should be compatible with A::say($num)
$b->say();

构造方法的重写

  • 构造方法重写规则与普通方法一样
  • 父类的构造函数会做很多初始化工作,重写构造函数会导致初始化工作完成不了,如操作数据库
  • 所以,如果子类继承时需要重写构造方法,保险一点,调用 parent::__construct()
class Mysql{
    protected $conn;
    public function __construct(){
        $this->conn = mysqli_connect('localhost','root','');
    }
}

class Mydb extends Mysql{
    public function __construct(){
        // 如果子类继承时重写了构造函数,保险一点
        parent::__construct();
        // 然后再加自己的业务逻辑
        echo '重写了构造函数';
    }
}

$mysql = new Mydb();

重写时,权限不能比父类严格

  • 对属性和方法都适用
class A{
    public function say($num){
        echo 'hello';
    }
}
class B extends A{
    // Fatal error: Access level to B::say() must be public (as in class A) 
    protected function say(){
        echo 'shit';
    }
}

父类的私有方法/属性不能被重写

  • 私有属性和方法会有其所在类的类名标志 [wife:B:private]=>凤姐 [wife:A:private]=>紫霞,其实并没有重写
  • 如果say() 方法是重写,子类和父类参数不一致,是会报错的!
class A{
    private $wife = '紫霞';
    private function say($num){
        echo 'hello';
    }
}
class B extends A{
    private $wife = '凤姐';
    public function say(){
        echo 'shit';
    }
}

$b = new B();
$b->say();      // shit
print_r($b);    // Object ([wife:B:private]=>凤姐 [wife:A:private]=>紫霞)

验证继承并不是复制

使用 __CLASS__返回当前的类名 验证

1.子类没对foo()方法重写,foo() 方法运行环境在A类中

// 若是复制,运行环境应该发生在B   
class A{
    public function foo(){
            echo '你正在运行',__CLASS__,'下的',__METHOD__,'方法',"\n";
    }
}

class B extends A{
}

$b = new B();
$b->foo();      // 你正在运行A下的A::foo方法

2.子类重写foo()方法,此时才真正将foo()方法写入B内存

class A{
    public function foo(){
            echo '你正在运行',__CLASS__,'下的',__METHOD__,'方法',"\n";
    }

}


class B extends A{
    public function foo(){
        echo '你正在运行',__CLASS__,'下的',__METHOD__,'方法',"\n";
    }
}

$b = new B();
$b->foo();      // 你正在运行B下的B::foo方法

私有属性继承后,子类添加同名私有属性,访问谁的问题?

案例一

父类和子类有同名私有wife属性,其实标签不同,不是重写

class Human{
    private $wife = '小仙女';
    public function show(){
        echo $this->wife,'<br>';	// 小仙女
    }
}

class Stu extends Human{
    private $wife = '凤姐';
    public function test(){
        echo $this->wife,'<br>';	// 凤姐
        $this->show();
        
    }
}

$lisi = new Stu();
$lisi->test();
print_r($lisi); // Stu Object([wife:Stu:private]=>凤姐 [wife:Human:private]=>小仙女)

终极解决解决办法,遇到private就给他加标签
在这里插入图片描述

案例二
将案例一中 子类中wife属性改为protected修饰

class Human{
    private $wife = '小仙女';
    public function show(){
        echo $this->wife,'<br>';    // 小仙女
    }
}

class Stu extends Human{
    protected $wife = '凤姐';
    public function test(){
        echo $this->wife,'<br>';    // 凤姐
        $this->show();

    }
}

$lisi = new Stu();
$lisi->test();
print_r($lisi); // Stu Object([wife:protected]=>凤姐 [wife:Human:private]=>小仙女)

终极解决解决办法,遇到private就给他加标签
在这里插入图片描述

案例三
去掉案例一子类中的wife属性

class Human{
    private $wife = '小仙女';
    public function show(){
        echo $this->wife,'<br>';
    }
}

class Stu extends Human{
    public function test(){
        echo $this->wife,'<br>';    // Notice: Undefined property: Stu::$wife
        $this->show();  // 小仙女
    }
}

$lisi = new Stu();
$lisi->test();
print_r($lisi); // Stu Object([wife:Human:private]=>小仙女)

终极解决解决办法,遇到private就给他加标签
在这里插入图片描述

案例四
去掉案例一父类中的wife属性

class Human{
    public function show(){
        echo $this->wife,'<br>'; // Fatal error: Uncaught Error: Cannot access private property Stu::$wife
    }
}

class Stu extends Human{
    private $wife = '凤姐';
    public function test(){
        echo $this->wife,'<br>';    // 凤姐
        $this->show();

    }
}

$lisi = new Stu();
$lisi->test();
print_r($lisi); // Stu Object([wife:Stu:private]=>凤姐)

在这里插入图片描述

案例五
父类的属性被子类重写覆盖

class Human{
    protected $wife = '小仙女';
    public function show(){
        echo $this->wife,'<br>';
    }
}

class Stu extends Human{
    protected $wife = '凤姐';
    public function test(){
        echo $this->wife,'<br>';    // 凤姐
        $this->show();  // 凤姐

    }
}

$lisi = new Stu();
$lisi->test();
print_r($lisi); // Stu Object([wife:protected] => 凤姐)

关于静态属性的继承

静态属性具有独立空间,为所有对象共有,因此一改全改,个人猜测!

// 没精力研究  留个代码观摩
class Base
{
    public static $var = 'var';

    public static function testStaticFun()
    {
        echo 'func';
    }
}

class A extends Base
{
    public function testSelf()
    {
        echo self::$var,'<br>';
    }

    public function testParent()
    {
        echo parent::$var,'<br>';
    }

    public function setSelf()
    {
        self::$var = 'self';
    }

    public function setParent()
    {
        parent::$var = 'parent';
    }

    public static function testStaticFun()
    {
        parent::testStaticFun();
        echo 'over','<br>';
    }
}

$objA = new A();

$objA->testSelf();    // var
$objA->testParent();  // var

$objA->setSelf();
$objA->testSelf();    // self
$objA->testParent();  // self
echo Base::$var,'<br>';      // self

$objA->setParent();
$objA->testSelf();    // parent
$objA->testParent();  // parent
echo Base::$var,'<br>';      // parent

Base::testStaticFun();    // func
A::testStaticFun();       // func over
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值