PHP与MySQL程序设计 学习笔记 第七章 高级OOP特性

php中不支持的OOP特性:
1.函数重载。
2.操作符重载。
3.多重继承。但支持实现多个接口。

php中对类对象的赋值号实际上是引用传递:

class a { 
    public $v = 3;
}

$aObj = new a();
$aCopy = $aObj;

$aCopy->v = 2;
print($aObj->v);    // 输出2

但对于标量类型来说是值传递:

$v1 = 2;
$v2 = $v1;

$v2 = 1;
print($v1);    // 输出2

可在对象前加clone关键字克隆对象:

class a {
    public $v = 3;
}

$aObj = new a();
$aCopy = clone $aObj;

$aCopy->v = 2;
print($aObj->v);    // 输出3

可在类中定义一个__clone方法调整对象的克隆行为:

class a {
    public $id = 3;
    
    public function __clone() {
        ++$this->id;
        
        $this->desc = "my id is $this->id.";
    }
}

$aObj = new a();
$aCopy = clone $aObj;

print($aCopy->id . "\n");
print($aCopy->desc);

运行它:
在这里插入图片描述
类继承:

class a {
    public $id = 3;
}

class b extends a {
    public $v = 1;
}

$bObj = new b();
print($bObj->id);    // 输出3

子类拥有它所有父类的属性和方法。

如果派生类没有构造函数而基类有构造函数,则派生类在实例化时会执行父类的构造函数:

class a {
    public $name;
    
    public function __construct($name) {
        $this->name = $name;
    }
}

class b extends a { }

$bObj = new b("myName");
print($bObj->name);    // 输出myName

$anotherBObj = new b();    // 会报错构造函数需要一个参数,而如果基类的构造函数不需要参数或基类没有构造函数,则不会报错

如果子类也有构造函数,当初始化子类时,无论父类是否有构造函数,都会执行子类的构造函数:

class a {
    public $name;
    
    public function __construct($name) {
        $this->name = $name;
    }
}

class b extends a { 
    public function __construct() {
        print("in child's constructor");
    }
}

$bObj = new b("myName");
print($bObj->name);

运行它:
在这里插入图片描述
可见父类的构造函数没有被执行,name成员的输出为空。派生类的构造函数会完全覆盖基类的构造函数。

php在构造对象时,即使没有显式定义构造函数也会构造成功:

class a {
    public $v = 2;
}

$aObj = new a(1);
print($aObj->v);    // 输出2

可在派生类中调用基类的构造函数:

class a {
    public $name;
    
    public function __construct($name) {
        $this->name = $name;
    }
}

class b extends a { 
    public function __construct($name) {
        print("in child's constructor\n");
        parent::__construct($name);
    }
}

$bObj = new b("myName");
print($bObj->name);

运行它:
在这里插入图片描述
当遇到parent::__construct($name)时,php会调用其直接父类的构造函数:

class x {
    public function __construct() {
        print("in x's constructor\n");
    }
}

class a extends x {
    public $name;
    
    public function __construct() {
        print("in a's constructor\n");
    }
}

class b extends a { 
    public function __construct() {
        print("in b's constructor\n");
        parent::__construct();
    }
}

$bObj = new b();

运行它:
在这里插入图片描述
如果x的构造函数更合适:

class x {
    public function __construct($name) {
        print("in x's constructor\n");
    }
}

class a extends x {
    public $name;
    
    public function __construct() {
        print("in a's constructor\n");
    }
}

class b extends a { 
    public function __construct($name) {
        print("in b's constructor\n");
        parent::__construct($name);
    }
}

$bObj = new b("test");

运行它:
在这里插入图片描述
如果上例中,类a的构造函数需要两个参数,则会报错a的构造函数需要两个参数,但只给了一个。

指明要调用的父类构造函数:

class x {
    public function __construct($name) {
        print("in x's constructor\n");
    }
}

class a extends x {
    public $name;
    
    public function __construct() {
        print("in a's constructor\n");
    }
}

class b extends a { 
    public function __construct($name) {
        print("in b's constructor\n");
        a::__construct($name);
        x::__construct($name);
    }
}

$bObj = new b("test");

运行它:
在这里插入图片描述
如果创建一个基类的间接派生类对象时,如果此基类的直接派生类和间接派生类都调用了此基类的构造函数,则按间接派生类的构造函数的调用顺序决定最后状态:

class x {
    public $name;
    
    public function __construct($name) {
        print("in x's constructor\n");
        $this->name = $name;
    }
}

class a extends x {
    public $name;
    
    public function __construct() {
        print("in a's constructor\n");
        x::__construct("ddd");
    }
}

class b extends a {
    public function __construct($name) {
        print("in b's constructor\n");
        x::__construct($name);
        a::__construct($name);
    }
}

$bObj = new b("test");
print($bObj->name);    // 输出test

/* ----------------------another code sequence------------------------*/
class x {
    public $name;
    
    public function __construct($name) {
        print("in x's constructor\n");
        $this->name = $name;
    }
}

class a extends x {
    public $name;
    
    public function __construct() {
        print("in a's constructor\n");
        x::__construct("ddd");
    }
}

class b extends a {
    public function __construct($name) {
        print("in b's constructor\n");
        a::__construct($name);    
        x::__construct($name);    // 上例代码中先调用的x构造函数再调用的a的构造函数
    }
}

$bObj = new b("test");
print($bObj->name);    // 输出ddd

可能一个父类方法要与静态类属性交互,但该类属性在一个子类中被覆盖:

class a {
    public static $v = 1;
}

class b extends a { 
    public static $v = 2;
}

$bObj = new b();
var_dump($bObj::$v);    // 输出2

但如果使用父类的静态方法:

class a {
    public static $v = 1;
    
    public static function func() {
        print(self::$v);
    }
}

class b extends a { 
    public static $v = 2;
}

$bObj = new b();
var_dump($bObj::$v);
$bObj::func();

运行它:
在这里插入图片描述
这是由于self在编译时确定其作用域而非运行时。php 5.3后可通过static改变此行为:

class a {
    public static $v = 1;
    
    public static function func() {
        print(static::$v);
    }
}

class b extends a { 
    public static $v = 2;
}

$bObj = new b();
var_dump($bObj::$v);
$bObj::func();

运行它:
在这里插入图片描述
基类方法的重写:

class a {
    public function func() { 
        print("in a's func");
    }
}

class b extends a {
    public function func() { 
        print("in b's func");
    }
}

$bObj = new b();
$bObj->func();    // 调用b的func

接口:

interface Ia {    // 接口名最好以I开头以表明它是接口
    public function func();
}

class a implements Ia {
    public function func() { }    // 必须实现接口中的所有函数,否则会报错,但也可将该类声明为抽象类以避免报错
}

接口中也可定义常量:

interface Ia {
    const v = 4;
    
    public function func();
}

class a implements Ia {
    // const v = 5;    // 错误,不能覆盖接口中的常量
    public function func() { }
}

$aObj = new a();
var_dump(a::v);    // 输出4,const对象访问时与static对象相似,只能通过类名或对象名的::访问
var_dump($aObj::v);    // 同上,输出4
var_dump($aObj->v);    // 错误,使用->不能访问类常量,这是访问类变量的方法,而类名没有->访问符

派生类也可实现接口:

interface Ia {
    const v = 4;
    
    public function func();
}

class a implements Ia {
    public function func() {
        print("in a");
    }
}

class b extends a {
    public function func() {
        print("in b");
    }
}

$bObj = new b();
$bObj->func();    // 调用b的func

一个类可实现多个接口:

interface Ia {
    public function func1();
}

interface Ib {
    public function func2();
}

interface Ic {
    public function func3();
}

class abc implements Ia, Ib, Ic {
    // 必须实现三个接口中的全部函数
}

抽象类不能被实例化,只能作为基类:

abstract class a { }    // 正确,抽象类中可以没有抽象方法
$aObj = new a();    // 错误,抽象类不能实例化
abstract class a {
    abstract public function func() { }    // 错误,抽象方法不能有函数体
}

抽象类的非抽象派生类必须实现该抽象类中的所有抽象方法:

abstract class a {
    abstract public function func();
}

class b extends a { }    // 错误,没有重写抽象方法func

使用抽象类还是接口:
1.如果所有类都会共享一个行为的实现,则使用抽象类并在其中实现该行为,接口中无法实现行为,因为接口中不能包含函数体。
2.如果要从多个来源继承行为,则使用接口,php不能继承自多个类。

随着不断创建类库和使用第三方类库,可能会出现两个类库使用了相同类名的情况,此时如果在脚本中同时require这两个库会报错。

从php 5.3开始,引入了命名空间,可在各库文件的最前面加上:

namespace 命名空间名;

之后在引用库文件时:

require "库文件"

use 命名空间名 as 短名;    // 如果命名空间名太长或想更改命名空间名可使用它

$obj = new 短名\类名();
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值