PHP面向对象2

多态

多态就是多种形态
多态分为:方法重载和方法重写,但是PHP不支持方法重载。

方法重写

子类方法和父类的方法必须同名

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?php
class animal{
public $name='动物';
public function jiao(){
echo $this->name."会叫<br>";
}
}

class dog extends animal{
public $name='狗';
public function jiao(){
echo $this->name."会叫<br>";
}
}

$dog = new dog();
$dog->jiao();

?>
var_dump($obj);
object(dog)#1 (1) { ["name"]=> string(3) "狗" }

1.子类重写的方法不能比父类的方法更加严格(封装)
public、protected、private
2.子类可以拥有和父类不同个数的参数,[严格标准是不允许的]

私有属性的继承和重写

私有属性可以被继承但不能被重写

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?php 
class animal{
private $name='动物';
public function jiao(){
echo $this->name."会叫<br>";
}
}

class dog extends animal{
private $name='狗';
public function jiao(){
echo $this->name."会叫<br>";
}
}

$dog = new dog();
var_dump($dog);
?>
object(dog)#1 (2) { ["name":"dog":private]=> string(3) "狗" ["name":"animal":private]=> string(6) "动物" }

访问修饰符

static 静态的
final 最终的
abstract 抽象的

static【静态的】

1、 static修饰属性叫静态属性,static修饰方法叫静态方法
2、 当类加载的时候就分配内存空间,在内存中就一份。不用实例化可以直接访问。为所有的对象共享。
3、 销毁对象不会销毁静态成员,因为静态成员属于类,而不属于对象。
4、 当页面执行完毕的时候才销毁静态成员。
5、 父类的static属性子类中可以继承并重写,但不能重新定义同名普通属性。
6、 静态方法中不能直接访问普通属性
7、 被self调用的普通方法会转成静态方法,【严格标准不允许】

1
2
3
4
5
6
7
8
9
10
11
12
<?php 
class Person{
public static $name = '人类';
public static function show(){
// echo '这个星球的主宰者是:'.$this->name;(报错:Fatal error: Using $this when not in object context in)
echo '这个星球的主宰者是:'.Person::$name;//(正确)
}
}

echo Person::$name,"<br>";
Person::show();
?>

静态成员可以被继承

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?php 
class Person{
public static $name = '人类';
public static function show(){
// echo '这个星球的主宰者是:'.$this->name;(报错:Fatal error: Using $this when not in object context in)
echo '这个星球的主宰者是:'.Person::$name;//(正确)
}
}

class Student extends Person{

}
echo Person::$name,"<br>";
Person::show();
echo "<br>";
echo Student::$name,'<br>';
Student::show();
echo '<br>';
?>

调用静态成员:类名::静态成员

self 表示当前类名
非静态方法被self调用,自动将非静态方法转成静态方法【严格标准是不允许的】

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?php 
class Teacher{
public function show(){
echo "stay hungry";
}
public function test(){
$obj = new self();
$obj->show();
}
}

Teacher::test();

?>

确定当前对象

1、$this表示当前对象的引用
2、self表示当前方法所在的类的类名
3、static表示当前对象所属的类的类名【静态延时绑定】
最后一次执行类的属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<?php
class Person {
public static $type='人类';
public function showPerson() {
//var_dump($this); //object(Student)#1 (0) { }
//echo self::$type; //人类
echo static::$type; //学生 静态延时绑定,表示Student
}
}
class Student extends Person {
public static $type='学生';
public function showStu() {
//var_dump($this); //object(Student)#1 (0) { }
//echo self::$type; //学生
echo static::$type; //学生
}
}
//测试
header('content-type:text/html;charset=utf-8');
$obj=new Student;
$obj->showPerson();
$obj->showStu();

final[最终的]

1、 final修饰的类不能被继承
2、 final修饰的方法不能被重写
3、 final不能修饰属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?php 
final class animal{
public $name='动物';
public function jiao(){
echo $this->name."会叫<br>";
}
}

class dog extends animal{
public $name='狗';
public function jiao(){
echo $this->name."会叫<br>";
}
}

$dog = new dog();
echo $dog->name,"<br>";
$dog->jiao();
?>

Fatal error: Class dog may not inherit from final class (animal) in
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?php 
class animal{
public $name='动物';
final public function jiao(){
echo $this->name."会叫<br>";
}
}

class dog extends animal{
public $name='狗';
public function jiao(){
echo $this->name."会叫<br>";
}
}

$dog = new dog();
echo $dog->name,"<br>";
$dog->jiao();
?>
Fatal error: Cannot override final method animal::jiao() in

abstract[抽象的]

1、 abstract修饰的类成为抽象类,abstract修饰的方法叫抽象方法
2、 抽象方法:只有方法的声明,没有方法的实现
3、 抽象类:类中只要有一个方法是抽象方法,这个类就是抽象类。
4、 抽象类不可以被实例化,必须在子类中重新实现。

抽象类的作用:定义方法的命名规范

接口(interface)

1、 如果一个类中所有的方法都是抽象方法,这个类就声明成接口。
2、 接口是一个特殊的抽象类
3、 接口中的方法只能是public的,默认是public的
4、 接口中的方法不能用abstract、final来修饰。
5、 定义过程用interface实现接口用的implements关键字

类不能多重继承,但是接口可以多重实现

继承类的同时实现接口

必须先继承类,再实现接口

class Myclass extends Class implements IPict1,IPict2

类常量【const】

类中可以放属性、方法、常量
调用类常量 类名::常量名
类常量属于类自身,不属于对象实例,不能通过对象实例访问
子类可以重写父类中的常量,可以通过(parent::)来调用父类中的常量

1
2
3
4
5
6
7
8
9
10
11
<?php 
class Student {
const type='学生'; //类常量
public function show() {
echo self::type,'<br>';
}
}
//测试
$stu=new Student;
$stu->show(); //学生
?>

类常量也可以放在接口中

1
2
3
4
5
6
<?php 
interface Person{
const type='人类';
}
echo Person::type;
?>

参数约束

1、 在PHP5.3以后才支持参数约束
2、 只能约束对象,不能约束基本数据类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?php 
class Student{
}
function show(Student $obj){
var_dump($obj);
}
show(10);

?>
Catchable fatal error: Argument 1 passed to show() must be an instance of Student, integer given, called in /Users/uiste/www/test.php on line 95 and defined in /Users/uiste/www/test.php on line 92
只能传递Student类型的数据

<?php
class Student{
}
function show(Student $obj){
var_dump($obj);
}
show(new Student);
?>
object(Student)#1 (0) { }

父类可以指向子类的引用

父类对象可以保存子类的地址

面向对象的三大特性:封装、继承、多态

类的自动加载

1、 一个文件中只能有一个类
2、 类文件名以.class.php结尾
3、 类名和文件名同名

Goods.class.php

1
2
3
4
5
6
7
8
9
10
11
12
<?php
/***商品类*/
abstract class Goods {
protected $name;
//设置商品名称
public function setName($name) {
$this->name=$name;
}
//获取商品名称
abstract function getName();
}
?>

Books.class.php

1
2
3
4
5
6
7
<?php
class Books extends Goods {
public function getName() {
echo "《{$this->name}》";
}
}
?>

Phone.class.php

1
2
3
4
5
6
7
<?php
class Phone extends Goods {
public function getName() {
echo $this->name;
}
}
?>

loadClass.php

1
2
3
4
5
6
7
8
9
10
<?php
header('content-type:text/html;charset=utf-8');
require './Goods.class.php';
require './Books.class.php';
require './Phone.class.php';
$book=new 45Books();
$phone=new Phone();
$book->setName('PHP高级');
$book->getName();
?>

手动引入类比较麻烦,不健壮。最好能实现需要什么类就自动加载什么类。

#### __autoload($class_name)
当页面执行的时候,PHP核心程序(Zend Engine)判断当前需要哪个类,如果没有找到该类,就自动的调用__autoload()函数,并且将缺少的类名作为参数传递到__autoload()函数中。

通过缺少类名来加载需要的类

1
2
3
4
5
6
7
8
9
<?php 
$book = new Books();
$book->setname("PHP高级");
$book->getname();

function __autoload($class_name){
require "./$class_name.class.php";
}
?>

将类映射到数组中

有的时候类存放的位置不规则,将不规则的地址映射到类名,形成一个数组再加载。

1
2
3
4
5
6
7
8
9
10
11
12
<?php 
function __autoload($class_name){
$map = array(
'Goods' => './aa/Goods.class.php',
'Books' => './bb/Books.class.php',
'Phone' => './cc/Phone.class.php'
);
if (isset($map[$class_name])) {
require $map[$class_name];
}
}
?>

利用命名规则加载类

同一类别的类放在同一个文件夹下,这样便于分组管理

将所有的类文件放到Lib文件夹下。将类名名字改为:类名+文件夹名

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
1.常规引入方法
require 'Goods.class.php';
require 'Book.class.php';
2.通过调用autoload自动加载类名,加载缺少类名的类
function __autoload($class_name){
require "./{$class_name}.class.php";
}
3.将类名与地址映射到关联数组中去加载
function __autoload($class_name){
$arr = array(
'Book'=>'Book.class.php',
'Phone'=>'Phone.class.php',
'Goods'=>'Goods.class.php'
);
if (isset($arr[$class_name]))
require "./{$arr[$class_name]}";
}
4.利用命名规则加载类一个项目中会有很多类,我们会将类分类存放,类的命令规则:在Lib文件夹下的类以Lib结尾,在Model文件夹的类以Model结尾。
function __autoload($class_name){
if (substr($class_name,-3)=='Lib')
require "./Lib/{$class_name}.class.php";
elseif (substr($class_name,-3)=='Control')
require "./Control/{$class_name}.class.php";
elseif (substr($class_name,-3)=='Model')
require "./Model/{$class_name}.class.php";

}

Final 和 abstract 不能修饰接口中的方法
参数约束只能约束对象