多态
- 多态(
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
}
抽象类和接口的区别
-
抽象类有普通的方法,接口没有
-
抽象类可以有自己的成员属性和方法,接口只能有public 常量。
-
抽象类可有可无构造方法,接口没有构造方法
-
抽象类单继承,接口多重继承
类型运算符
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();