我们可以想象,宇宙由太阳,地球,月亮等不同的物体组成。同样,我们可以想象汽车由不同的物体组成,如轮子,转向,齿轮等。同样的方式是面向对象的程序设计概念, 用不同的对象来实现一个软件。
面向对象的概念
在详细介绍之前,我们先来了解与定义面向对象编程有关的重要术语。
- 类 - 这是一个程序员定义的数据类型,它包括本地函数以及本地数据。可以把一个类看作一个模板,用来创建同一类型(或类)对象的许多实例。
- 对象 - 由类定义的数据结构的单个实例。定义一个类,然后制作属于它的许多对象。 对象也被称为实例。
- 成员变量 - 这是在一个类中定义的变量。 这些数据在课堂以外是不可见的,可以通过成员函数访问。 创建对象后,这些变量被称为对象的属性。
- 成员函数 - 这是在类中定义的函数,用于访问对象数据。
- 继承 - 当一个类是通过继承父类的现有函数来定义的,那么就称它为继承。 这里的子类将继承父类的全部或少数成员函数和变量。
- 父类 - 从另一个类继承的类。 这也被称为基类或超类。
- 子类 - 从另一个类继承的类。 这也被称为子类或派生类。
- 多态 - 这是一个面向对象的概念,其中相同的功能可以用于不同的目的。 例如函数名称将保持不变,但它使参数的数量不同,可以做不同的任务。
- 重载 - 一种多态的类型,其中一些或全部的操作符具有不同的实现,这取决于它们的参数的类型。 类似的功能也可以通过不同的实现来重载。
- 数据抽象 - 隐藏(抽象)实现细节的数据的任何表示。
- 封装 - 是指将所有数据和成员函数封装在一起形成一个对象的概念。
- 构造函数 - 指的是一个特殊类型的函数,每当有一个类的对象形成时,它将被自动调用。
- 析构函数 - 指的是一种特殊类型的函数,每当一个对象被删除或超出范围时,都会被自动调用。
定义PHP类
在PHP中定义一个新类的一般形式如下 -
<?php
class phpClass {
var $var1;
var $var2 = "constant string";
function myfunc ($arg1, $arg2) {
[..]
}
[..]
}
?>
下面是上述每一行的代码描述 -
- 指定由
class
关键字构成,后面跟着想要定义的类的名字。 - 包含任意数量的变量声明和函数定义的一组大括号。
- 变量声明以特殊形式
var
开头,后面跟着一个常规的$
变量名称; 也可能有一个初始分配到一个值。 - 函数定义看起来很像独立的PHP函数,但是对于类是局部的,将用于设置和访问对象数据。
示例
下面是定义一个名称为:Book
的类的例子 -
<?php
class Books {
/* 成员变量 */
var $price;
var $title;
/* 成员函数 */
function setPrice($par){
$this->price = $par;
}
/* 成员函数 */
function getPrice(){
echo $this->price ."<br/>";
}
/* 成员函数 */
function setTitle($par){
$this->title = $par;
}
/* 成员函数 */
function getTitle(){
echo $this->title ." <br/>";
}
}
?>
变量$this
是一个特殊的变量,它指向同一个对象,即对象本身。
在PHP中创建对象
当定义了类以后,那么就可以创建尽可能多的对象。 以下是如何使用new
运算符创建对象的示例。
$physics = new Books; // 或者 $physics = new Books();
$maths = new Books;// 或者 $maths = new Books();
$chemistry = new Books;// 或者 $chemistry = new Books();
这里创建了三个对象,这些对象是相互独立的,它们分开存在。 接下来看看如何访问成员函数和处理成员变量。
创建对象之后,能够调用与该对象相关的成员函数。 一个成员函数只能处理相关对象的成员变量。
以下示例显示了如何通过调用成员函数来为三本书设置标题和价格。
$physics->setTitle( "高中物理" );
$chemistry->setTitle( "高级化学" );
$maths->setTitle( "代数" );
$physics->setPrice( 10 );
$chemistry->setPrice( 15 );
$maths->setPrice( 7 );
现在调用另一个成员函数来获取上例中设置的值 -
$physics->getTitle();
$chemistry->getTitle();
$maths->getTitle();
$physics->getPrice();
$chemistry->getPrice();
$maths->getPrice();
完整的代码如下所示 -
<?php
class Books {
/* 成员变量 */
var $price;
var $title;
/* 成员函数 */
function setPrice($par){
$this->price = $par;
}
/* 成员函数 */
function getPrice(){
echo $this->price ."<br/>";
}
/* 成员函数 */
function setTitle($par){
$this->title = $par;
}
/* 成员函数 */
function getTitle(){
echo $this->title ." <br/>";
}
}
$physics = new Books; // 或者 $physics = new Books();
$maths = new Books;// 或者 $maths = new Books();
$chemistry = new Books;// 或者 $chemistry = new Books();
$physics->setTitle( "高中物理" );
$chemistry->setTitle( "高级化学" );
$maths->setTitle( "代数" );
$physics->setPrice( 10 );
$chemistry->setPrice( 15 );
$maths->setPrice( 7 );
$physics->getTitle();
$chemistry->getTitle();
$maths->getTitle();
$physics->getPrice();
$chemistry->getPrice();
$maths->getPrice();
?>
执行上面示例代码,得到以下结果 -
高中物理
高级化学
代数
10
15
7
构造函数
构造函数是特殊类型的函数,无论何时创建对象,函数都会自动调用。 所以我们充分利用这个行为,通过构造函数初始化很多东西。
PHP提供了一个叫做__construct()
的特殊函数来定义一个构造函数。可以像构造函数那样传递参数。
以下示例将为Books
类创建一个构造函数,它将在创建对象时初始化该书的价格和标题。
function __construct( $par1, $par2 ) {
$this->title = $par1;
$this->price = $par2;
}
现在不需要分别调用set
函数来设置价格和标题。只能在创建对象时初始化这两个成员变量。 查看下面的例子 -
$physics = new Books( "高中物理", 10 );
$maths = new Books ( "高级化学", 15 );
$chemistry = new Books ("代数", 7 );
/* 获取上面构造函数创建的值 */
$physics->getTitle();
$chemistry->getTitle();
$maths->getTitle();
$physics->getPrice();
$chemistry->getPrice();
$maths->getPrice();
执行上面示例代码,得到以下结果 -
高中物理
高级化学
代数
10
15
7
析构函数
像构造函数一样,可以使用__destruct()
函数定义析构函数。可以在析构函数中释放所有的资源。
继承
PHP类定义可以使用extends
子句从父类定义中继承。 语法如下 -
class Child extends Parent {
// 定义类的变量和方法
}
继承的结果是一个子类(或子类或派生类)具有以下特征 -
- 自动拥有父类的所有成员变量声明,子类可以定义自己的成员变量。
- 自动具有所有与父代相同的成员函数,子类可以定义自己的函数。
以下示例将继承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/>";
return $this->price;
}
function getTitle(){
echo $this->title . "<br/>";
return $this->title;
}
公共成员
除非另外指定,否则类的属性和方法是公共的。 也就是说,它们可能在三种可能的情况下被访问 -
- 从它声明的类外
- 从它声明的类内
- 从另一个实现它所声明的类的类中
到目前为止,我们已经看到所有成员都是公共(public
)成员。 如果想限制一个类的成员的可访问性,那么可将类成员定义为private
或protected
。
私有会员
通过指定一个私有成员,可以限制它被声明的类的可访问性。 私有成员不能从继承它所声明的类的类中引用,并且不能从类之外访问。
可以通过使用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!");
}
}
当MyClass
类被另一个类使用extends
继承时,myPublicFunction()
函数将是可见的,$driver
也是可见的。 扩展类将不能访问myPrivateFunction
和$car
,因为它们被声明为私有(private
)。
受保护的成员
受保护的属性或方法可以在声明的类中以及扩展该类的类中访问。受保护的成员不在这两种类别之外访问。 可以在成员的前面使用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
public function sendMail(){
echo 'Send email...';
}
}
常量
常量有点像一个变量,因为它有一个值,但是更像一个函数,因为一个常量是不可变的。当声明一个常量,它不会改变。
声明一个常量很容易,就像在下面这个MyClass
类代码中所做的一样 -
class MyClass {
const requiredMargin = 1.98;
function __construct($incomingValue) {
// Statements here run every time
// an instance of the class
// is created.
}
}
在这个类中,requiredMargin
是一个常量。 它是用关键字const
声明的,在任何情况下都不能它的值。 请注意,常量的名称没有前导$
,因为变量名称。
抽象类
抽象类是不能被实例化的,只能被继承。抽象类是使用关键字abstract
来声明的。
当从一个抽象类继承时,父类的声明中标记为抽象的所有方法都必须由子进行定义; 另外,这些方法必须用相同的可见性来定义。
abstract class MyAbstractClass {
abstract function myAbstractFunction() {
}
}
请注意,抽象类中的函数定义也必须以关键字abstract
开头。 抽象函数定义在非抽象类中是不合法的。
static关键字
将类成员或方法声明为静态,这使得它们不需要类的实例化就能访问。声明为静态的成员不能使用实例化的类对象进行访问(尽管可以使用静态方法)。
看看下面的例子 -
<?php
class Foo {
public static $my_static = 'foo';
public function staticValue() {
return self::$my_static;
}
}
print Foo::$my_static . "
";
$foo = new Foo();
print $foo->staticValue() . "
";
?>
final关键字
PHP 5引入了叫做:final
的关键字,它通过使用final
前缀来定义方法以防止子类重写这个方法。 如果这个类本身被定义为final
,那么它就不能被扩展。
以下示例导致致命错误:无法重写final
方法BaseClass::moreTesting()
<?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()
函数。
注 - 构造函数可以使用与类名相同的名称来定义。它在上面的例子中定义。