PHP面向对象程序设计
面向对象的程序设计(Object Oriented Programming)
一、面向对象程序设计的概念
PHP引进的面向对象的设计方法,将数据及处理数据的相应函数“封装”到一个“类(class)”中。类的实例称为“对象”。在一个对象内,只有属于该对象的函数才可以存取该对象的数据。
面向对象的程序设计有三个主要特征:封装、继承和多态。
1.封装
封装是将数据和代码捆绑到一起,避免外界的干扰和不确定性。在PHP中,封装是通过类来实现的。类是抽象数据类型的实现,一个类的所有对象都具有相同的数据结构,并且共享相同的实现操作的代码,而各个对象又有着各自不同的状态,即私有的存储。因此,类是所有对象的共同的行为和不同状态的结合体。
由一个特定的类所创建的对象称为这个类的实例,因此类是对象的抽象及描述,它是具有共同行为的若干对象的统一描述体。类中还包含生成对象的具体方法。
2.继承
类提供了创建新类的一种方法,再借助于“继承”,这一重要机制扩充了类的定义,实现了面向对象的优越性。
继承提供了创建新类的方法:一个新类可以通过对已有的类进行修改或扩充来满足新类的需求。新类共享已有类的行为,而自己还具有修改的或额外添加的行为。因此,可以说继承的本质特征是行为共享。
从一个类继承定义的新类,将继承已有类的所有方法和属性,并且可以添加所需要的新的方法和属性。新类被称为已有类的子类,已有类称为父类,又叫基类。
3.多态
不同的类对于不同的操作具有不同的行为,称为多态。多态机制使具有不同的内部结构的对象可以共享相同的外部接口,通过这种方式减少代码的负责度。
二、在PHP中创建类、属性和方法
类是面向对象程序设计的核心,它是一种数据类型。类由变量和函数组成,在类里面,变量称为属性或成员变量,函数称为方法。定义类的语法格式如下:
class classname
{
[var $property[=value];...]
[function functionname($args)
{
//代码
}
]
}
在PHP中,类需要使用class关键字来定义,后面跟类名classname。类的命名规则要符合PHP标记的命名规则,并且不能是PHP的保留字。类名后跟一对花括号,花括号里面包含类的属性和方法。
在类中,使用关键字var来声明变量,即类的属性。使用关键字function来定义函数,即类的方法。
注意:在定义属性时,给属性赋的值不能使用定界符,不能是表达式,不能包含括号,不能通过其他变量赋值。另外,在类的方法内部的变量只是普通变量,不属于类的属性,因为它的作用范围只限于方法内部。不能将类的定义放到多个文件或多个PHP块中。
三、类的实例化与访问
在声明一个类后,类只存在于文件中,程序不能调用。需要创建一个对象后程序才能使用,创建一个类对象的过程叫做类的实例化。类的实例化使用new关键字,关键字后面需要指定实例化的类名。
在一个类中,可以访问一个特殊的指针“$this”。如果当前类有一个属性$attribute,则在类的内部要访问这个属性时,可以使用“$this->attribute”来引用。
在对象呗创建之后,可以在类的外部对该对象的属性和方法进行访问,访问的方法是在对象后面使用“->”符合加上要访问的属性和方法。注意属性的前面没有“$”。
说明:有的类对类中的属性进行了访问控制,如果设置了类外部不能访问类中属性时,直接访问将会发生错误。
四、类的访问控制
在PHP5中,访问修饰符public、private和protected,它们可以控制属性和方法的可见性,通常放置在属性和方法的声明之前。
①public。声明为公用的属性和方法,可以在类的外部或内部进行访问。public是默认选项,如果没有为一个属性或方法制定修饰符,那么它将是public的。
②private。声明为私有的属性和方法,只可以在类的内部进行访问,私有的属性和方法将不会被继承。
③protected。声明为被保护的属性和方法,只可以在类的内部和子类的内部进行访问。
在进行类的设计时,通常将类的属性设为私有的,而将大多数方法设为公有的。这样,类以外的代码不能直接访问类的私有数据,从而实现了数据的封装,而公有的方法可为内部的私有数据提供外部接口,但接口实现的细节在类外又是不可见的。
五、静态属性和方法
所谓“静态”是指所定义的属性和方法与类的实例无关,只与类本身有关。静态的属性和方法一般用于包含类要封装的数据和功能,可以由所有类的实例共享。在类里面可以使用static关键字定义静态属性和方法。
访问静态属性和方法时需要使用到范围解析符“::”,格式如下:
classname::$attribute; //访问静态属性
classname::Cfunction([$args,...]) //访问静态方法
注意:只有静态的属性和方法才能使用范围解析符,不能使用范围解析符访问非静态的属性和方法。
六、构造函数和析构函数
构造函数是类中的一个特殊函数,当创建一个类的实例时,构造函数将被自动调用,主要功能通常是对类中的对象完成初始化工作。与构造函数相对的是析构函数,析构函数在类的对象被销毁时被自动调用。
①构造函数。在PHP5中,构造函数的名称为“__construct”,"contruct"前面是两根下划线。如果一个类同时拥有__contruct构造函数和类名相同的函数,PHP5将把__construct看做是构造函数。PHP中的构造函数可以带参数,也可以不带参数。
②析构函数。类的析构函数的名称是__destruct,如果在类中声明了__destruct函数,PHP将在对象被销毁前调用析构函数将对象从内存中销毁,节省服务器资源。
如果构造函数中带了参数,在创建对象时也可以在类名后面为构造函数指定参数。
七、类的继承
1.子类访问父类
在PHP中,允许通过继承其他类的方法来调用这些类里已经定义好的属性和方法,PHP不支持多继承,所以一个子类只能继承一个父类。可以使用extends关键字来指明类与类之间的关系。
类B继承与类A,所以类B的对象可以访问类A的属性和方法,但A中的private的属性和方法将不能被继承,A中protected的属性和方法只能在B的内部访问,类B创建的对象无法访问,否则将产生错误。B作为A的子类,具有类A一样的数据和功能,此外B还可以声明自己的属性和方法。
继承是单方向的,子类可以从父类中继承特性,但父类却无法从子类中继承特性。
如果子类中没有自己的构造函数,那么子类在实例化时会自动调用父类的构造函数。如果子类中有自己的构造函数,则执行自己的构造函数。
如果要在子类中调用父类的方法,除了使用“$this->”外,还可以使用parent关键字加范围解析符,如“parent::functionname()”。建议使用后一种方法,因为前面的方法容易造成子类和父类方法结构不清。而对于父类的属性,在子类中只能使用“$this->”来访问,属性是不区分父类和子类的。
继承可以是多重的。例如:类B继承了类A,类C继承了类B,那么类C也就继承了类A和类B的父类的所有特性。
2.重载
方法的重载是指在一个类中可以定义多个拥有相同名称的方法,通过参数个数和类型来区分这些方法。在PHP中可以通过类的继承,在子类中定义和父类中相同名称的方法来实现类似方法重载的特性。
3.使用final关键字
PHP中final关键字,在声明类时使用这个关键字,将使这个类不能被继承。另外,如果将final关键字用于声明类中的方法,该方法将不能在任何子类中重载。
注意:final关键字只能用于声明类和方法。
八、抽象类和接口
1.抽象类
抽象类是一种特殊的类,使用关键字abstract来定义,不能被实例化。一个抽象类中至少包含一个抽象方法,抽象方法也是由abstract关键字来定义。抽象方法只提供了方法的声明,不提供方法的实现。例如:
abstract function func($name,$number);
包含抽象方法的类必须是抽象类。
抽象类不能用于创建对象,所以只能通过继承来使用。继承抽象类的子类,必须重载抽象类中的所有抽象方法才能被实例化。
2.接口
PHP只能提供单继承,即一个类只能有一个父类。接口是一个特殊的抽象类,使用interface关键字取代class关键字来定义。抽象类中允许存在非抽象的方法和属性,而在接口中定义的方法都是抽象方法。在接口中不能使用属性,但可以使用const关键字定义的常量。例如:
const con="tom";
接口的定义方法和定义类的方法类似,在接口中定义抽象方法不适用abstract关键字。
注意:接口中所有的方法都要求使用public定义,由于默认的访问修饰符就是public,所以可以省略。
接口和类一样,也支持继承,接口之间的继承也使用extends关键字。例如:
<?php
interface A
{
const name="";
function show();
}
interface B extends A
{
function getname();
}
?>
定义了接口之后可以将其实例化,接口的实例化称为接口的实现。要实现一个接口需要一个子类来实现接口的所有抽象方法。定义接口的子类使用implements关键字,另外,一个子类还可以实现多个接口,这样就解决了多继承的问题。
一个子类还可以同时继承一个父类和多个接口。
九、类的魔术方法
PHP规定以两个下划线“__”开头的方法都保留为魔术方法,所以在定义函数名时尽量不要用"__"开头,除非是为了重载已有的魔术方法。
1.克隆对象
PHP使用clone关键字建立一个与原对象拥有相同属性和方法的对象,这种方法用于通过一个类实例化两个类的对象的情况下。例如:
$new_obj=clone $old_obj;
$new_obj是新的对象名,$old_obj是要克隆的对象名。
克隆后的对象拥有克隆对象的全部属性,如果需要改变这些属性,可以使用PHP提供的魔术方法__clone。这个方法在克隆一个对象时将被自动调用,类似于__construct和__destruct方法。在__clone方法中,可以定义确切的复制行为或执行一些操作。
2.方法重载
PHP5中有一个魔术方法__call,该方法可以用于实现方法的重载。__call方法必须要有两个参数。第一个参数包含被调用的方法名称,第二个参数包含传递给该方法的参数数组。__call方法在类中的方法被访问时被调用。
3.访问类的属性
通常情况下,从类的外部直接访问类的属性是不建议使用的方法。这时可以使用__get和__set方法来检查和设置属性的值,这样就可以实现封装性。
4.字符串转换
如果要输出对象,可以在类中定义一个__toString方法,在方法中返回一个可输出的字符串。
5.自动加载对象
__autoload方法用于自动加载对象,它不是一个类方法,而是一个单独的函数。如果脚本中定义了__autoload函数,当时用new关键字实例化一个没有声明的类时,这个类的名字将作为参数传递给__autoload函数,__autoload函数根据参数自动包含有类的文件,并加载类文件中的同名类。
6.对象序列化
对象序列化是指将一个对象转换成字节流的形式,将序列化后的对象在一个文件或网络上传输,然后再反序列化还原为原数据。对象序列化使用serialize()函数,反序列化使用unserialize()函数。在对象序列化时,如果存在魔术方法__sleep,PHP会调用__sleep方法,主要用于清楚如数据提交、关闭数据库链接等工作,并返回一个数组,该数组包含需要序列化的所有变量;在反序列化一个对象后,PHP会调用__wakeup方法,主要用于重建对象序列化时丢失的资源。这两个魔术方法都不接收参数。
十、实例类型的判断
当系统很大时,往往需要判断某个对象是否是某个类创建的。这时可以使用instanceof关键字来实现。instanceof关键字可以检查一个对象的类型,判断一个对象是否是特定类的实例,是否继承于某个类或实现某个接口。用法如下:
$var instanceof class_name;
如果变量$var是类class_name创建的对象,则返回TRUE,否则返回FALSE。