我们可以想象宇宙由太阳、地球、月亮等不同的物体组成。同样,也可以想象汽车由不同的物体组成,如车轮、转向、齿轮等。同样,还有 面向对象 编程概念,它们将所有内容都视为一个对象,并使用不同的对象实现软件。
面向对象的概念
在我们详细讨论之前,下面是面向对象编程相关的重要术语。
术语 | 描述 |
---|---|
类(Class ) | 程序员定义的数据类型,包括本地函数和本地数据。你可以把一个类看作是一个模板,用于创建相同类型(或类)对象的许多实例。 |
对象(Object ) | 由类定义的数据结构的单个实例。您定义一个类一次,然后创建许多属于它的对象。对象也称为 instance。 |
成员变量(Member Variable) | 这些是在类中定义的变量。此数据对类的外部不可见,并且可以通过成员函数访问。创建对象后,这些变量称为对象的属性。 |
成员函数(Member function) | 这些是在类中定义的函数,用于访问对象数据。 |
继函(Inheritance) | 当一个类是通过继承父类的现有函数来定义的时,它被称为继承。这里,子类将继承父类的所有或少量成员函数和变量。 |
父类(Parent class) | 由另一个类继承的类。这也称为基类或超类。 |
子类(Child Class) | 从另一个类继承的类。这也称为子类或派生类。 |
多态性(Polymorphism) | 这是一个面向对象的概念,其中相同的函数可以用于不同的目的。例如,函数名称将保持不变,但它需要不同数量的参数,并且可以执行不同的任务。 |
重载(Overloading) | 一种多态性,其中部分或全部运算符根据其参数的类型具有不同的实现。同样,函数也可以使用不同的实现来重载。 |
数据抽象(Data Abstraction) | 隐藏(抽象)实现细节的任何数据表示形式。 |
封装(Encapsulation) | 是指我们将所有数据和成员函数封装在一起以形成一个对象的概念。 |
构造函数(Constructor) | 是指一种特殊类型的函数,每当从类中形成对象时,它就会被自动调用。 |
析构函数(Destructor) | 是指一种特殊类型的函数,每当对象被删除或超出范围时,它将被自动调用。 |
定义 PHP 类
PHP 定义新类的一般形式如下 -
<?php
class phpClass {
var $var1;
var $var2 = "常量字符串";
function myfunc ($arg1, $arg2) {
[..]
}
[..]
}
?>
以下是每行的描述 -
- 特殊格式类,后跟要定义的类的名称。
- 一组大括号,包含任意数量的变量声明和函数定义。
- 变量声明以特殊形式 var 开头,后跟约定俗成的 $ 变量名称,它们也可能具有对 常量 值的初始赋值。
- 函数定义看起来与独立的 PHP 函数非常相似,但对于类来说是本地的,将用于设置和访问对象数据。
例子
下面是一个定义 Books 类型的类的示例 -
<?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;
$maths = 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 类创建一个构造函数,它将在创建对象时初始化书籍的 price 和 title。
function __construct( $par1, $par2 ) {
$this->title = $par1;
$this->price = $par2;
}
现在我们不需要单独调用 set 函数来设置 price 和 title。我们只能在创建对象时初始化这两个成员变量。检查下面的示例 -
$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 {
<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/>";
return $this->price;
}
function getTitle(){
echo $this->title . "<br/>";
return $this->title;
}
公共成员
除非另行指定,否则类的属性和方法是 public 的。也就是说,可以在三种可能的情况下访问它们 -
- 从声明它的类外部
- 从声明它的类中
- 从另一个实现声明它的类的类中
到目前为止,我们已将所有成员视为公共成员。如果要限制类成员的可访问性,则可以将类成员定义为 private 或 protected。
私有成员
通过指定成员 private,可以将其可访问性限制为声明它的类。不能从继承声明私有成员的类中引用私有成员,也不能从类外部访问该成员。
可以通过在成员前面使用 private 关键字将类成员设为私有。
class MyClass {
private $car = "skoda";
$driver = "SRK";
function __construct($par) {
// 这里的语句每次都会运行
// 类的实例
// 创建
}
function myPublicFunction() {
return("能看见!");
}
private function myPrivateFunction() {
return("在外面看不见!");
}
}
当 MyClass 类被另一个使用 extends 的类继承时,myPublicFunction() 将可见,$driver 也将可见。扩展类将不知道或访问 myPrivateFunction 和 $car,因为它们被声明为 private。
受保护成员
受保护的属性或方法可以在声明它的类中访问,也可以在扩展该类的类中访问。受保护成员在这两种类型的类之外不可用。可以通过在成员前面使用 protected 关键字来保护类成员。
这是 MyClass 的不同版本 -
class MyClass {
protected $car = "skoda";
$driver = "SRK";
function __construct($par) {
//这里的语句每次都会运行
//类的一个实例
//已创建
}
function myPublicFunction() {
return("能看见!");
}
protected function myPrivateFunction() {
return("在子类中可见!");
}
}
接口
定义接口是为了向实现者提供通用函数名称。不同的实现者可以根据他们的要求实现这些接口。可以说,接口是由开发人员实现的框架。
从 PHP5 开始,可以定义一个接口,如下所示 -
interface Mail {
public function sendMail();
}
然后,如果另一个类实现了该接口,例如: -
class Report implements Mail {
// sendMail()定义在这里
}
常量
常量有点像变量,因为它保存一个值,但实际上更像一个函数,因为常量是不可变的。声明常量后,它不会更改。
声明一个常量很容易,就像在这个版本的 MyClass 中所做的那样 -
class MyClass {
const requiredMargin = 1.7;
function __construct($incomingValue) {
//这里的语句每次都会运行
//类的一个实例
//已创建。
}
}
在此类中,requiredMargin 是一个常量。它是用关键字 const 声明的,在任何情况下都不能更改为 1.7 以外的任何内容。
请注意,常量名称不像变量名称那样具有前导 $。
抽象类
抽象类是不能实例化的类,只能被继承的类。您使用关键字 abstract 声明一个抽象类,如下所示 -
从抽象类继承时,父类声明中标记为 abstract 的所有方法都必须由子类定义,此外,这些方法必须具有相同的可见性。
abstract class MyAbstractClass {
abstract function myAbstractFunction() {
}
}
注意:抽象类中的函数定义也必须在关键字 abstract 前面。在非抽象类中包含抽象函数定义是不合法的。
static 关键字
将类成员或方法声明为 static 使它们无需实例化类即可访问。声明为 static 的成员不能使用实例化的类对象进行访问(尽管 static 方法可以)。
试试下面的例子 -
<?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";
?>
最终关键词
PHP 5 引入了 最终(final) 关键字,它可以防止子类通过在定义前加上 final 来覆盖方法。如果类本身被定义为 final,则无法扩展。
以下示例导致致命错误:无法覆盖最终方法 BaseClass::moreTesting()
<?php
class BaseClass {
public function test() {
echo "BaseClass::test() 称呼<br>";
}
final public function moreTesting() {
echo "BaseClass::moreTesting() 称呼<br>";
}
}
class ChildClass extends BaseClass {
public function moreTesting() {
echo "ChildClass::moreTesting() 称呼<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() 函数。
注意: 可以使用与类名称相同的名称来定义构造函数,在上面的示例中定义了它。