[PHP从小白到大牛]-034 PHP-面向对象(二)

1,199 阅读4分钟

多态

  • 多态(Polymorphism)按字面的意思就是“多种状态”
  • 接口的多种不同的实现方式即为多态
  • 同一操作作用于不同的对象,可以有不同的解释,产生不同的执行结果。

抽象类

  • 声明一个抽象类, 声明关键字abstract
  • 抽象的方法只是声明了其调用方式(参数),不能定义其具体的功能实现。方法只写名字, 不写具体内容
  • 抽象类不能被实例化
  • 抽象方法, 必须定义在抽象类里面
  • 抽象类可以为空, 可以不包含抽象方法

声明一个抽象类

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

具体类继承并且实现

<?php

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

class ChildClass extends MyAbsClass
{
	// 这样写会报错, 因为没有实现抽象类中定义的方法
}
<?php

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

class ChildClass extends MyAbsClass
{
    public function func()
    {
        // 即便什么都没有写, 也没有关系...
    }
}

如果是抽象类继承, 则不用实现抽象方法

<?php

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

abstract class ChildClass extends MyAbsClass
{
	// 什么都没写, 也不会报错...
}

抽象类, 必须有抽象方法

abstract class MyAbsClass 
{
    public function func(); // 会报错, 因为没有abstract关键字
}

同理, 含有抽象方法的类, 必须是抽象类

class MyAbsClass // 会报错, 因为没有abstract关键字
{
    abstract public function func(); 
}

抽象类, 必须有抽象方法, 至少有一个即可

<?php

abstract class MyAbsClass
{
    public $name = '张三';
    public $age;

    public function test()
    {
        echo 'hello';
    }

    abstract public function func();
}

修饰关键字顺序

  • 类的属性和方法必须添加访问修饰符(private、protected以及public)
  • abstract以及final必须声明在访问修饰符之前
  • abstract和final不能共存
  • static必须声明在访问修饰符之后

final public static function

接口

  • 使用接口(interface),可以指定某个类必须实现哪些方法,但不需要定义这些方法的具体内容。

  • 接口中也可以定义常量。

  • 接口实现关键字implements

<?php

interface MyInterface
{
    const NUM = 123;

    public function func();

    public function test();
}

class MyClass implements MyInterface
{
    public function func()
    {

    }

    public function test()
    {
        echo 'hello';
    }
}
<?php

interface MyInterface
{
    const NUM = 123;

    public function func();

    public function test()
    {
        echo "test"; // 会报错, 不能有具体方法
    };
}


<?php

interface MyInterface
{
    const NUM = 123;

    public function func();

    public function test();
}

class MyClass implements MyInterface
{
    public function func()
    {

    }

	// 报错, 因为没有实现test()
}
  • 抽象类实现接口, 可以不用实现其中的方法, 但是抽象类的子类必须实现
<?php

interface MyInterface
{
    const NUM = 123;

    public function func();

    public function test();
}

abstract class MyClass implements MyInterface
{
	// 没有实现接口的方法, 也不会报错
}

class Demo extends MyClass
{
    public function func()
    {
        // 必须实现, 否则报错
    }

    public function test()
    {
        // 必须实现, 否则报错
    }
}
  • 接口也可以继承关键字extends, 如果接口B继承了接口A, 而普通类 C实现了接口B, 则A和B所有的方法, C都要实现

<?php

interface A
{
    public function aaa();

}

interface B extends A
{
    public function bbb();
}

class C implements A
{
    public function bbb()
    {
		// 
    }

    public function aaa()
    {

    }
}
  • 一个类可以实现多个接口, 两个接口中的方法都需要实现
<?php

interface A
{
    public function aaa();

}

interface B
{
    public function bbb();
}

class C implements A, B
{
    public function bbb()
    {
		// 都得实现, 不然报错
    }

    public function aaa()
    {
		// 都得实现, 不然报错
    }
}
  • 继承和实现可以同时使用
<?php

interface A
{
    public function aaa();

}

interface B
{
    public function bbb();
}

class C
{
    public function bbb()
    {

    }

    public function aaa()
    {

    }
}

class D extends C implements A, B
{
	// 类D继承C, C实现了A和B
}

抽象类和接口的区别

  1. 抽象类有普通的方法,接口没有

  2. 抽象类可以有自己的成员属性和方法,接口只能有public 常量。

  3. 抽象类可有可无构造方法,接口没有构造方法

  4. 抽象类单继承,接口多重继承

类型运算符

  • instanceof用于确定一个PHP变量是否属于某一类class的实例
<?php


class A
{

}

class B
{

}

$a = new A();

var_dump($a instanceof A); // true
var_dump($a instanceof B); // false
  • instanceof也可用来确定一个变量是不是继承自某一父类的子类的实例
<?php


class A extends B
{

}

class B
{

}

$a = new A();

var_dump($a instanceof A); // true
var_dump($a instanceof B); // true
  • instanceof也可用于确定一个变量是不是实现了某个接口的对象的实例
<?php


class A implements B
{

}

interface B
{

}

$a = new A();

var_dump($a instanceof A); // true
var_dump($a instanceof B); // true

类型约束

  • PHP5可以使用类型约束。
  • 函数的参数可以指定其类型
<?php


class A
{

}

class B
{
    
}

$a = new A();


$b = new B();


function test(A $n)
{
    echo "ok";
}

test($a); // 输出ok
test($b); // 报错, 因为$b是B的实例, 不是A的实例
  • 如果一个类或接口指定了类型约束,则其所有的子类或实现也都如此
<?php


class A
{
    public function aaa(B $n)
    {

    }
}

class B
{

}

class C extends B
{
    public $a = '123';
}

class D extends A
{

}

$a = new A();
$c = new C();
$a->aaa($c);

自动加载(需要的类在不同文件)

  • __autoload()函数也能自动加载类和接口
  • spl_autoload_register()函数可以注册任意数量的自动加载器,
  • 当使用尚未被定义的类(class)和接口(interface)时自动去加载。
  • 建议使用spl_autoload_register()函数
  • 不再建议使用__autoload()函数,在以后的版本中它可能被弃用

C:\Users\Administrator\Desktop\imooc\a.class.php

<?php

class A extends B
{
    // 会报错, 因为找不到B
}

C:\Users\Administrator\Desktop\imooc\b.class.php

<?php

class B
{

}

引入即可

<?php
include 'b.class.php'; // 引入即可
class A extends B
{
    // 会报错, 因为找不到B
}

使用自动加载

<?php

function myloader()
{
    echo '被执行了...';
    include 'b.class.php';
}

spl_autoload_register('myloader'); // 执行myloader

class A extends B
{
    
}

使用静态方法

<?php


class Loader
{
    public static function myloader()
    {
        echo '被执行了...';
        include 'b.class.php';
    }
}


spl_autoload_register(['Loader', 'myloader']); // 执行myloader

class A extends B
{
    // 不会报错
}

也可以做成构造方法

<?php


class Loader
{
    public function __construct()
    {
        spl_autoload_register([$this, 'myloader']);
    }

    public function myloader()
    {
        echo '被执行了...';
        include 'b.class.php';
    }
}


$obj = new Loader();

class A extends B
{
   
}

接口同理...

类, 抽象类, 对象, 接口的关系

命名空间的基本使用

命名空间概述

  • 从广义上来说,命名空间是一种封装事物的方法
  • 在很多地方都可以见到这种抽象概念
  • 例如,在操作系统中目录用来将相关文件分组,对于目录中的文件夹来说,它就扮演了命名空间的角色

编程中常见的两类问题

  • 用户编写的代码与PHP内部的类/函数/常量或第三方类/函数/常量之间的名字冲突
    • 比如引入的文件中, 有当前文件已经使用的类/函数/常量名
  • 为很长的标识符名称(通常是为了缓解第一类问题而定义的)创建一个别名(或简短)的名称,提高源代码的可读性

定义命名空间

  • 命名空间通过关键字namespace来声明。
  • 如果一个文件中包含命名空间,它必须在其它所有代码之前声明命名空间
namespace Project1;
// 代码...

namespace Project2{
    // 代码...
}
<?php

namespace MySapce {
    function test()
    {
        echo 'test1';
    }

    test(); // 不会报错
}

namespace MySapce2 {
    function test()
    {
        echo 'test2';
    }

    test(); // 不会报错
}
<?php

namespace myspace;
function time()
{
    echo 'hello';
}

time(); // 不会报错, 输出hello
<?php

namespace myspace1;
function time()
{
    echo 'hello1';
}

time(); // 不会报错, 输出hello1

namespace myspace2;
function time()
{
    echo 'hello2';
}

time(); // 不会报错, 输出hello2

指定命名空间

<?php

namespace myspace1;
function time()
{
    echo 'hello1';
}


namespace myspace2;
function time()
{
    echo 'hello2';
}

\myspace1\time(); // hello1

使用系统的time方法, \表示全局

<?php

namespace myspace1;
function time()
{
    echo 'hello1';
}


namespace myspace2;
function time()
{
    echo 'hello2';
}

echo \time(); // 1567210241 系统函数, 返回时间戳

定义命名空间

  • 声明单个命名空间namespace MyProject

  • 定义子命名空间namespace MyProject\Sub\Level;

  • 可以在同一个文件中定义多个命名空间

  • 如果没有定义任何命名空间,所有的类与函数的定义都是在全局空间,与PHP引入命名空间概念前一样。

  • 在名称前加上前缀\表示该名称是全局空间中的名称,即使该名称位于其它的命名空间中时也是如此

__NAMESPACE__常量

常量__NAMESPACE__的值是包含当前命名空间名称的字符串

在全局的,不包括在任何命名空间中的代码,它包含一个空的字符串。

<?php

namespace hello\hello1;
var_dump(__NAMESPACE__); // hello\hello1
namespace hello\hello2;
var_dump(__NAMESPACE__); // hello\hello2
<?php

var_dump(__NAMESPACE__); // ""

非限定名称, 完全限定名称, 限定名称(绝对路径, 相对路径)

<?php

namespace A\B;
class MyClass
{
    public function __construct()
    {
        echo '空间A\B 中的类 实例化了' . "\n";
    }
}

namespace A;
class MyClass
{
    public function __construct()
    {
        echo '空间A 中的类 实例化了' . "\n";
    }
}

$obj = new MyClass();// 非限定名称 就近
$obj = new \A\B\MyClass();// 完全限定名称 绝对路径
$obj = new \A\MyClass();// 完全限定名称 绝对路径
$obj = new B\MyClass();//限定名称 相对路径

include不会改变当前命名空间, 但是include之后, 可以使用引入文件中的命名空间

C:\Users\Administrator\Desktop\imooc\aaa.php

<?php

namespace aaa;

function demo()
{
    echo 'aaa';
}

C:\Users\Administrator\Desktop\imooc\bbb.php

<?php

namespace bbb;

function demo()
{
    echo 'bbb';
}

include 'aaa.php';
demo(); // bbb
var_dump(__NAMESPACE__); // bbb
\aaa\demo(); // 可以使用aaa.php的命名空间

动态调用命名空间

动态调用函数

<?php


function demo()
{
    echo "demo\n";
}

demo(); // 调用demo

$a = 'demo';
$a(); // 同样可以调用demo

动态实例化对象

<?php


class A
{
    public function demo()
    {
        echo "demo\n";
    }
}

$a = new A();
$a->demo();

$b = "A";
$c = new $b;
$c->demo();

动态调用命名空间

<?php

namespace A\B;

function demo()
{
    echo "demo\n";
}

namespace A;

demo(); // 会报错, 因为当前命名空间下, 没有demo()
<?php

namespace A\B;

function demo()
{
    echo "demo\n";
}

namespace A;

\A\B\demo(); // 这就会正常了
<?php

namespace A\B;

function demo()
{
    echo "demo\n";
}

namespace A;
$a = '\A\B\demo'; // 效果一样, 因为都是完全限定名称
$a = 'A\B\demo';
$a(); 
<?php

namespace A\B;

function demo()
{
    echo "demo\n";
}

namespace A;
$a = 'B\demo'; // 报错, 不支持相对路径
$a();