面向对象2
1. ==自动加载==
引言:
早期的开发过程中,程序开发人员通常会将所有的可能要使用的类文件通过include的方式,引入到当前的程序脚本中。但是这么做,会使得程序加载效率过低,拖慢程序的整体执行速度。
比如,当前有三个类,A、B、C,分别定义在三个文件中,A类定义在A.class.php中,B类定义在B.class.php中,C类定义在C.class.php中。当构建一个新的项目页面时,为了操作方便,将会事先把三个类文件全部引入进项目页面,如将三个类文件引入进test.php中。
那么,如果test.php中只使用到了一个类,而另外两个类在整个项目里都没用到,则会造成解析test.php程序代码时额外的开销和浪费。
所以,在PHP中,可以使用”自动加载”这种技术方式,来弥补这样的缺陷。
“自动加载”==技术的思路==是:什么时候要用到这个类,就将这个类所在的类文件引入进项目中来。
触发执行自动加载的==时机==:当==使用到==某个类时,而当前整个程序脚本中又没有这个类的定义,就会触发PHP执行走自动加载,寻找类的定义。
默认的自动加载
引言:PHP中是通过一个==固定名字的函数==来实现默认的自动加载的。
==需求==:使用code/prepare目录中的A、B、C类文件,在code/cod3.php中通过默认的自动加载函数实现加载A类和B类的类文件。
==解答==:在code3.php中构建代码如下:
1 |
|
访问code1.php,效果为:
==小结==:
- PHP不负责定义默认的自动加载函数,只负责调用这个函数;
- 调用默认自动加载函数的时机:当使用到一个类,而这个类的定义又不存在时,则会触发PHP自动调用执行一次这个函数,传递使用的这个类的类名给该函数;
自定义自动加载
引言:除了默认的自动加载,PHP还支持使用自定义的自动加载。
涉及的函数:
spl_autoload_register(混合型参数) 自动加载注册函数
在PHP中,自定义的自动加载有三种实现方式:
- 自定义函数
- 自定义非静态方法
- 自定义静态方法
==注意==:无论使用哪一种方式,都需要用到spl_autoload_register函数。
自定义函数
==需求==:使用code/prepare目录中的A、B、C类文件,在code/code4.php中通过自定义函数实现自动加载A类和B类的类文件。
==解答==:在code4.php中构建代码如下:
1 |
|
访问code4.php,效果为:
==小结==:
- 函数名可以任意指定,但是也需要一个必填参,这个参数将会接收PHP传递的某个类的类名;
- 要使用自定义函数做自动加载,必须要使用spl_autoload_register注册自动加载函数;
自定义非静态方法
==需求==:使用code/prepare目录中的A、B、C类文件,在code/code5.php中通过自定义非静态方法实现自动加载A类和B类的类文件。
==解答==:在code5.php中构建代码如下:
1 |
|
访问code1.php,效果为:
==小结==:
- 如果要使用非静态方法实现自动加载,则必须先创建一个基于该方法所在类的对象;
- 然后在spl_autoload_register函数中指定一个数组参数,数组的第一个元素必须是在”1”中创建的对象,第二个元素必须是该对象下的方法的方法名;
自定义静态方法
==需求==:使用code/prepare目录中的A、B、C类文件,在code/code6.php中通过自定义静态方法实现自动加载A类和B类的类文件。
==解答==:在code6.php中构建代码如下:
1 |
|
访问code6.php,效果为:
==小结==:
- 如果要使用静态方法实现自动加载,则无需事先创建对象,只需要在spl_autoload_register中指定这个类的类名及该类中的静态方法名即可;
总结
- 如果是面向过程的方式开发项目,则默认的自动加载函数用的多;如果使用的是基于面向对象的框架方式开发,则静态方法用的多;
- 如果使用了自定义的自动加载,则默认的自动加载函数将会失效,要想将默认的自动加载函数重新恢复效果,则需要再次使用spl_autoload_register函数将默认的自动加载函数重新注册一次;
- 自定义自动加载可以注册多个,但是每次使用spl_autoload_register只能注册一个;
2. ==类的继承==
引言:程序是模拟现实世界的,现实世界中存在着继承的概念,故程序中也有相应的模拟实现方式。
==概念==:属于类A的成员,同时也属于类B,我们就说类B==继承==了类A。
继承的语法
==实现语法==:
1 | //extends是关键字 |
==需求==:使用code/prepare目录中的Animal.class.php类文件,在code/code8.php中通过构建Birds类实现继承Animal类,要求,
- 给Birds类构建非静态属性$firther,值为:有羽毛;
- 实例化一个基于Birds类的对象保存给$bird,打印该对象;
- 测试通过$bird对象调用move方法;
==解答==:在code8.php中构建代码如下:
1 |
|
访问code8.php,效果为:
==小结==:
- 继承是通过关键字extends来实现的;
- 实现了继承后,子类中也就拥有了父类的成员;
继承中的三个概念
父类:就是被继承的那个类;
派生:派生是一个==过程==,通过一个已有的类,产生一个新类的过程,就叫派生;
子类:继承别的类的这个类(==子类也叫派生类==);
==提问==:到目前为止,我们从效果上观察到,通过继承,可以使子类也能拥有父类的成员,那么,实现继承的过程,是否意味着在程序中将父类的成员直接拷贝给了子类呢?
继承链
==原理图==:
==小结==:
- 通过继承,实际上将会在编译过程和之后实例化对象的过程中形成一种关系,其中在对象空间中形成的如上图所示青绿色部分的空间关系内容,就是继承链部分,相当于在#1大空间下分别开辟小的空间分门别类的保存不同的成员;
parent关键字
作用:parent关键字就是专门在==子类中==代替==父类的类名==的。
==需求==:使用code/prepare目录中的Father.class.php类文件,在code/code9.php中通过构建Son类实现继承Father类,要求,
- 在Son类中创建一个名为test的非静态方法;
- 在test方法中打印父类的$desposit属性;
- 在test方法中访问singing方法;
==解答==:在code9.php中构建代码如下:
1 |
|
访问code9.php,效果为:
==小结==:
- paernt关键字,在存在继承关系的子类中,专门用来代替父类的类名的;
访问限定修饰符
引言:访问限定修饰符,顾名思义就是一种符号,专门用来限定用户访问的。
在PHP中,访问限定修饰符共有三个:
- public 表示公有的
- protected 表示受保护的
- private 表示私有的
定义方式:
1 |
|
被不同的访问限定修饰符所修饰的成员,将具有不同的特性。
访问限定修饰符的特性
三种访问限定修饰符的特性分别为:
- public 表示在类的内部,类的外部和类的继承链中都能==直接==访问;
- protected 表示在类的内部和继承链中能被==直接==访问;
- private 表示只能在本类的内部被==直接==访问;
public
==需求1==:使用code/prepare目录中的PublicTest.class.php类文件中的类,要求,
- 测试在PublicTest类的外部访问pubic修饰的四个成员;
==解答1==:在code10.php中构建代码如下:
1 |
|
访问code10.php,效果为:
==需求2==:使用code/prepare目录中的PublicTest.class.php类文件中的类,要求,
- 在PublicTest类中创建一个名为test的测试方法,测试在PublicTest类的内部访问pubic修饰的四个成员;
==解答2==:在codex11.php中构建代码如下:
1 |
|
访问code11.php,效果为:
==需求3==:使用code/prepare目录中的PublicTest.class.php类文件中的类,要求,
- 再创建一个名为PublicTestSon的类继承PublicTest类,在PublicTestSon类中创建son_test方法,测试在继承链中访问PublicTest类中pubic修饰的四个成员;
==解答3==:在code12.php中构建代码如下:
1 |
|
访问code12.php,效果为:
==小结==:
- 被public所修饰的成员,可以在 类的内部、类的外部 和 继承链中 被==直接==访问;
protected
==需求1==:使用code/prepare目录中的ProtectedTest.class.php类文件中的类,要求,
- 测试在ProtectedTest类的外部访问protected修饰的四个成员;
==解答1==:在code13.php中构建代码如下:
1 |
|
访问code13.php,效果为:
==需求2==:使用code/prepare目录中的ProtectedTest.class.php类文件中的类,要求,
- 在ProtectedTest类中创建一个名为test的测试方法,测试在ProtectedTest类的内部访问protected修饰的四个成员;
==解答2==:在code14.php中构建代码如下:
1 |
|
访问code14.php,效果为:
==需求3==:使用code/prepare目录中的ProtectedTest.class.php类文件中的类,要求,
- 再创建一个名为ProtectedTestSon的类继承ProtectedTest类,在ProtectedTestSon类中创建son_test方法,测试在继承链中访问ProtectedTest类中protected修饰的四个成员;
==解答3==:在code15.php中构建代码如下:
1 |
|
访问code15.php,效果为:
==小结==:
- 被protected修饰的成员,只能在 类的内部 和 继承链中 被==直接==访问;
private
==需求1==:使用code/prepare目录中的PrivateTest.class.php类文件中的类,要求,
- 测试在PrivateTest类的外部访问private修饰的四个成员;
==解答1==:在code16.php中构建代码如下:
1 |
|
访问code16.php,效果为:
==需求2==:使用code/prepare目录中的PrivateTest.class.php类文件中的类,要求,
- 在PrivateTest类中创建一个名为test的测试方法,测试在PrivateTest类的内部访问private修饰的四个成员;
==解答2==:在code17.php中构建代码如下:
1 |
|
访问code17.php,效果为:
==需求3==:使用code/prepare目录中的PrivateTest.class.php类文件中的类,要求,
- 再创建一个名为PrivateTestSon的类继承PrivateTest类,在PrivateTestSon类中创建son_test方法,测试在继承链中访问PrivateTest类中private修饰的四个成员;
==解答3==:在code18.php中构建代码如下:
1 |
|
访问code18.php,效果为:
==小结==:
- 被private修饰的成员,只能在本类的内部被==直接==访问;
继承的特点
类只能单继承,不能多继承,但是能够形成一个长串的继承链;
演示代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class A{
public $var1='A';
}
class B extends A{//让B继承A
public $var2='B';
}
class C extends B{//让C继承B,则因为B继承了A,所以C也会拥有A中继承过来的成员
public $var3='C';
}
$obj = new C;
var_dump( $obj );执行演示代码,输出如下所示:
类只能继承父类中的非私有成员(不能使用但是可以看);
3. ==重写==(override)
概念:所谓的重写,指的就是对父类中==已有方法的重新定义==。
==需求==:使用code/prepare目录中的Animal.class.php类文件,在code/code21.php中通过构建Birds类和Fish类实现继承Animal类,要求,
- 给Birds类构建非静态属性$firther,值为:有羽毛;给Fish类构建非静态属性$scale,值为:有鱼鳞;
- 在Birds类中重写父类的move方法,并测试使用;
- 在Fish类中重写父类的move方法,并测试使用;
==解答==:在code21.php中构建代码如下:
1 |
|
访问code21.php,效果为:
==小结==:
- 重写可以保证老的代码的完整性不会被破坏;
- 重写就是对父类中已有方法的重新定义;
魔术方法重写问题
引言:我们可以通过重写的方式达到即不改动老的代码,又能改进功能的目的,但是在类中,有一种方法一旦被重写,将会导致出现问题,这个方法就是魔术方法。
==需求1==:使用code/prepare目录中的Father1.class.php类文件,在code/code23.php中通过构建Son类实现继承Father1类,要求,
- Son类中构建两个非静态属性,分别为$son_name、$son_age,不指定初始值;
- Son类中构建构造方法为$son_name、$son_age赋值,创建基于Son类的对象,打印该对象查看效果;
- 解决Son中的构造方法对父类构造方法的重写问题;
==解答1==:在code23.php中构建代码如下:
1 |
|
访问code23.php,效果为:
==小结1==:
- 当前的例子是以构造方法被重写为案例的,其实解决魔术方法被重写的方式和解决构造方法被重写的方式是一样的,解决魔术方法重写的方式如下:
1 | parent::方法名([形参列表]); |
4. ==final关键字==
在php中,final关键字可以用来定义两种东西:
- 类 使用final关键字定义的类,我们称其为 ==final类==或==最终类==
- 类中的方法 使用final关键字定义的类中的方法,我们称其为 ==final方法==或==最终方法==
最终类
==实现语法==:
1 | //使用final关键字申明类 |
==演示案例==:构建code25.php程序文件,代码如下:
1 |
|
访问code25.php,效果为:
==小结==:
- 如果是final申明的类,则这个不能被继承;
最终方法
==实现语法==:
1 | //使用final关键字申明方法 |
==演示案例==:构建code26.php程序文件,代码如下:
1 |
|
访问code26.php,效果为:
==小结==:
- 被final声明的方法不能被子孙类重写(override);
5. ==抽象类==
==实现语法==:
1 | //使用abstract申明一个抽象类 |
抽象类的成员
抽象类的成员包含两个部分:
- 普通类的所有成员
- 抽象方法
定义方式:
1 |
|
抽象类的特点
抽象类==只能被继承==,==不能被==直接==实例化==为对象,但是抽象类中的静态成员和类常量都能够正常使用;
演示代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
abstract class AbstractDemo{
#普通类的成员
public $var1='var1';
public static $var2='var2';
const URL='www.home.com';
public function f1(){
echo 'f1';
}
public static function f2(){
echo 'f2';
}
#抽象方法
abstract public function f3($v1, $v2=100);
}
#抽象类不能用来实例化成对象
//$obj = new AbstractDemo;
var_dump( AbstractDemo::$var2 ); //访问静态属性
echo '<br/>';
AbstractDemo::f2(); //访问静态方法
echo '<br/>';
var_dump( AbstractDemo::URL ); //访问类常量访问测试代码,输出效果为:
抽象类如果被普通类所继承,那么抽象类中的抽象方法必须==全部==被实现;
演示代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
abstract class A{
#抽象方法
abstract public function f3($v1, $v2=100);
abstract public function f4();
}
class B extends A{
public function f3($v1, $v2=100){ //实现了父类中的抽象方法f3
}
}访问测试代码,输出效果为:
抽象类还可以被抽象类所继承,如果被抽象类所继承,那么被继承的那个抽象类中其抽象方法可以不被实现;
演示代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
abstract class A{
#抽象方法
abstract public function f2();
abstract public function f3($v1, $v2=100);
}
abstract class B extends A{//抽象类继承抽象类,则父类中的抽象方法可以不被实现
abstract public function f4();
public function f2(){
}
}
7. 全天总结
使用静态方法实现自动加载。
步骤:
1)定义一个静态方法,实现自动加载功能;
2)使用spl_autoload_register将这个静态方法注册成为自动加载方法;
继承的特点,
- 继承只能单继承(只能有一个爸爸),不能多继承,但是可以实现长串的继承链(可以有爸爸,也可以有爷爷,曾爷爷。。。);
- 继承只能继承非私有的成员,私有的成员能看到,但是不能使用;
访问限定修饰符
- public 表示公有的,被public修饰的成员,能够在类的外部,类的内部和类的继承链中被==直接==访问;
- protected 表示受保护的,被protected修饰的成员,只能在类的内部和类的继承链中被==直接==访问;
- private 表示私有的,被private修饰的成员,只能在本类的内部被==直接==访问;
构造方法如果被重写,可以使用固定的一个结果:parent::__construct([实参列表])来避免构造方法被重写;
final关键字如果声明的是一个类,则该类不能再被继承;如果声明的是一个方法,则方法不能被重写;