PHP 面向对象编程
我们可以想象我们的宇宙由不同的对象组成,像太阳、地球、月亮等等。类似地,我们可以想象我们的汽车由轮子、方向盘、齿轮等不同的对象组成。同样地,面向对象编程概念将一切都视为对象,并使用不同的对象来实现软件。
面向对象概念
在我们深入讨论之前,先定义与面向对象编程相关的重要术语。
- 类 - 这是一种由程序员定义的数据类型,包含本地函数和本地数据。您可以将类看作是制造许多相同类型(或类)的对象实例的模板。
-
对象 - 由类定义的数据结构的单个实例。您只需定义一次类,然后创建许多属于该类的对象。对象也称为实例。
-
成员变量 - 这些是定义在类内部的变量。这些数据对类的外部是不可见的,但可以通过成员函数来访问。一旦创建了对象,这些变量就称为对象的属性。
-
成员函数 - 这些是在类内部定义的函数,用于访问对象数据。
-
继承 - 当一个类通过继承现有的父类函数来定义时,就被称为继承。子类将继承父类的所有或部分成员函数和变量。
-
父类 - 被另一个类继承的类。这也称为基类或超类。
-
子类 - 继承另一个类的类。这也称为子类或派生类。
-
多态 - 这是面向对象的一个概念,同一个函数可以用于不同的目的。例如,函数名称保持不变,但接受不同数量的参数并可以执行不同的任务。
-
重载 - 这是一种多态性,其中一些或所有操作符根据其参数类型具有不同的实现。类似地,函数也可以使用不同的实现进行重载。
-
数据抽象 - 任何表示数据的方式,其中隐藏了实现细节(抽象化)。
-
封装 - 指的是将所有的数据和成员函数封装在一起形成一个对象的概念。
-
构造函数 - 指的是一种特殊类型的函数,当从一个类中形成对象时,会自动调用该函数。
-
析构函数 - 指的是一种特殊类型的函数,当一个对象被删除或超出范围时,会自动调用该函数。
定义PHP类
在PHP中定义一个新类的一般形式如下:
<?php
class phpClass {
var var1;
varvar2 = "constant string";
function myfunc (arg1,arg2) {
[..]
}
[..]
}
?>
以下是每行的说明:
- 特殊形式 class ,后面跟着你要定义的类的名称。
-
一对大括号,内部包含任意数量的变量声明和函数定义。
-
变量声明以特殊形式 var 开头,后面跟着一个常规的$变量名;它们也可以有初始赋值给一个常量值。
-
函数定义看起来很像独立的PHP函数,但是它们是局部的类函数,用于设置和访问对象数据。
示例
以下是一个定义了Books类型的类的示例:
<?php
class Books {
/* Member variables */
var price;
vartitle;
/* Member functions */
function setPrice(par){this->price = par;
}
function getPrice(){
echothis->price ."<br/>";
}
function setTitle(par){this->title = par;
}
function getTitle(){
echothis->title ." <br/>";
}
}
?>
变量 $this
是一个特殊的变量,它指的是同一个对象,即它本身。
在PHP中创建对象
一旦你定义了类,你就可以创建多个属于该类类型的对象。下面是使用 new 操作符创建对象的示例。
$physics = new Books;
$maths = new Books;
$chemistry = new Books;
在这里,我们创建了三个对象,这些对象是相互独立的,它们将分别存在。接下来,我们将看到如何访问成员函数和处理成员变量。
调用成员函数
创建对象后,您将能够调用与该对象相关的成员函数。一个成员函数只能处理其相关对象的成员变量。
以下示例展示了如何通过调用成员函数为这三本书设置标题和价格。
$physics->setTitle( "Physics for High School" );
$chemistry->setTitle( "Advanced Chemistry" );
$maths->setTitle( "Algebra" );
$physics->setPrice( 10 );
$chemistry->setPrice( 15 );
$maths->setPrice( 7 );
现在你调用另一个成员函数来获取上面示例中设置的值 –
$physics->getTitle();
$chemistry->getTitle();
$maths->getTitle();
$physics->getPrice();
$chemistry->getPrice();
$maths->getPrice();
这将产生以下结果−
Physics for High School
Advanced Chemistry
Algebra
10
15
7
构造函数
构造函数是一种特殊类型的函数,当对象被创建时会自动调用。因此,我们通过构造函数可以初始化许多事物。
PHP提供了一个特殊的函数称为 __construct()
来定义一个构造函数。您可以在构造函数中传递任意多个参数。
以下示例将为Books类创建一个构造函数,并在对象创建时初始化图书的价格和标题。
function __construct( par1,par2 ) {
this->title =par1;
this->price =par2;
}
现在我们不需要分别调用set函数来设置价格和标题。我们只需要在对象创建时初始化这两个成员变量。请查看以下示例−
$physics = new Books( "Physics for High School", 10 );
$maths = new Books ( "Advanced Chemistry", 15 );
$chemistry = new Books ("Algebra", 7 );
/* Get those set values */
$physics->getTitle();
$chemistry->getTitle();
$maths->getTitle();
$physics->getPrice();
$chemistry->getPrice();
$maths->getPrice();
这将产生以下结果 −
Physics for High School
Advanced Chemistry
Algebra
10
15
7
析构函数
类似于构造函数,您可以使用函数 __destruct() 来定义析构函数。您可以在析构函数中释放所有资源。
继承
PHP类定义可以选择性地通过使用extends子句从父类定义中继承。语法如下所示 –
class Child extends Parent {
<definition body>
}
继承的影响是子类(或子类或派生类)具有以下特点 −
- 自动具有父类的所有成员变量声明。
-
自动具有与父类相同的所有成员函数,默认情况下这些函数的工作方式与父类中的函数相同。
以下示例继承了Books类,并根据需求添加了更多功能。
class Novel extends Books {
var publisher;
function setPublisher(par){
this->publisher =par;
}
function getPublisher(){
echo $this->publisher. "<br />";
}
}
现在除了继承的函数之外,类Novel还保留了两个额外的成员函数。
函数重写
子类中的函数定义会覆盖父类中相同名称的定义。在子类中,我们可以修改从父类继承的函数的定义。
在下面的示例中,getPrice和getTitle函数被重写以返回一些值。
function getPrice() {
echo this->price . "<br/>";
returnthis->price;
}
function getTitle(){
echo this->title . "<br/>";
returnthis->title;
}
公共成员
除非您另行指定,类的属性和方法都是公共的。也就是说,它们可以在以下三种情况下访问:
- 在声明该类之外的地方
-
在声明该类的内部
-
在实现该类的另一个类的内部
到目前为止,我们已经把所有成员都视为公共成员。如果您希望限制类的成员的可访问性,那么您可以将类成员定义为 私有 或 受保护 。
私有成员
通过将成员指定为私有,您限制了它只能在声明它的类中访问。私有成员不能从继承该类的类中引用,也不能从类外部访问。
可以使用 private 关键字将类成员设置为私有成员。
class MyClass {
private car = "skoda";driver = "SRK";
function __construct($par) {
// Statements here run every time
// an instance of the class
// is created.
}
function myPublicFunction() {
return("I'm visible!");
}
private function myPrivateFunction() {
return("I'm not visible outside!");
}
}
当另一个类使用extends继承MyClass类时,myPublicFunction()和driver将可见。扩展的类将无法意识到或访问myPrivateFunction和car,因为它们被声明为私有。
保护成员
受保护的属性或方法可在声明它的类中访问,以及扩展该类的类中访问。受保护的成员在这两种类型的类之外不可用。通过在成员前面使用 protected 关键字,可以将类成员设置为受保护。
这是MyClass的另一个版本 –
class MyClass {
protected car = "skoda";driver = "SRK";
function __construct($par) {
// Statements here run every time
// an instance of the class
// is created.
}
function myPublicFunction() {
return("I'm visible!");
}
protected function myPrivateFunction() {
return("I'm visible in child class!");
}
}
接口
接口是为实现者提供共同的函数名称。不同的实现者可以根据自己的需求实现这些接口。可以说,接口是由开发人员实现的骨架。
从PHP5开始,可以像这样定义一个接口−
interface Mail {
public function sendMail();
}
然后,如果另一个类实现了该接口,就像这样 –
class Report implements Mail {
// sendMail() Definition goes here
}
常量
常量有点像变量,因为它保存一个值,但更像一个函数,因为常量是不可变的。一旦声明了一个常量,它就不会改变。
声明一个常量很简单,就像在这个版本的MyClass中做的一样−
class MyClass {
const requiredMargin = 1.7;
function __construct($incomingValue) {
// Statements here run every time
// an instance of the class
// is created.
}
}
在这个类中,requiredMargin是一个常量。它使用关键字const声明,在任何情况下都不能改变为除了1.7之外的其他值。注意,常量的名称没有前导的$,和变量的名称不同。
抽象类
抽象类是不能被实例化的,只能被继承。你可以使用关键字 abstract 来声明一个抽象类,如下所示:
当继承一个抽象类时,父类声明中所有标记为抽象的方法必须由子类来定义;此外,这些方法必须具有相同的可见性。
abstract class MyAbstractClass {
abstract function myAbstractFunction() {
}
}
注意,在抽象类中定义的函数也必须以abstract关键字开头。在非抽象类中定义抽象函数是不合法的。
静态关键字
将类成员或方法声明为静态可以在不实例化类的情况下访问它们。声明为静态的成员不能使用实例化的类对象访问(但静态方法可以)。
尝试下面的示例 −
<?php
class Foo {
public static my_static = 'foo';
public function staticValue() {
return self::my_static;
}
}
print Foo::my_static . "\n";foo = new Foo();
print $foo->staticValue() . "\n";
?>
Final关键字
PHP 5引入了final关键字,通过在方法定义前加上final来阻止子类覆盖方法。如果类本身被定义为final,则无法被继承。
以下示例会导致致命错误:无法覆盖BaseClass::moreTesting()的final方法。
<?php
class BaseClass {
public function test() {
echo "BaseClass::test() called<br>";
}
final public function moreTesting() {
echo "BaseClass::moreTesting() called<br>";
}
}
class ChildClass extends BaseClass {
public function moreTesting() {
echo "ChildClass::moreTesting() called<br>";
}
}
?>
调用父构造函数
不需要为子类编写全新的构造函数,我们可以通过显式调用父类的构造函数来编写子类的构造函数,并在子类实例化时进行其他必要的操作。这里是一个简单的示例:
class Name {
var _firstName;
var_lastName;
function Name(first_name,last_name) {
this->_firstName =first_name;
this->_lastName =last_name;
}
function toString() {
return(this->_lastName .", " .this->_firstName);
}
}
class NameSub1 extends Name {
var _middleInitial;
function NameSub1(first_name, middle_initial,last_name) {
Name::Name(first_name,last_name);
this->_middleInitial =middle_initial;
}
function toString() {
return(Name::toString() . " " . $this->_middleInitial);
}
}
在这个示例中,我们有一个父类(Name),它有一个带有两个参数的构造函数,还有一个子类(NameSub1),它有一个带有三个参数的构造函数。NameSub1的构造函数通过使用::语法显式调用其父类构造函数(传递两个参数),然后设置一个额外的字段。类似地,NameSub1通过覆盖的父类函数来定义它的非构造函数toString()函数。
注意 - 构造函数可以使用与类名相同的名称进行定义。在上面的示例中已经定义了。