本章我们将继续描述 PHP 中的面向对象编程。
PHP static
关键字
我们可以将类属性和方法声明为static
。 static
属性和方法不属于该类的实例。 他们属于阶级本身。 可通过范围解析运算符::
访问它们。
staticmethod.php
<?php
class Sys {
public static function println(string) {
echo "string\n";
}
}
Sys::println("PHP");
Sys::println("PERL");
Sys::println("Python");
Sys::println("Pike");
在上面的 PHP 脚本中,我们有一个静态的println()
方法。 它打印一个字符串并开始新的一行。 此示例受 Java 语言启发。
Sys::println("PHP");
我们不需要对象来调用println()
方法。 我们通过指定类名称,后跟双冒号运算符和方法名称来调用static
方法。
$ php static1.php
PHP
PERL
Python
Pike
这是脚本的输出。
staticvariable.php
<?php
class Math {
public static PI = 3.14159265359;
}
echo Math::PI . "\n";
现在,我们有一个带有static
变量的示例。
echo Math::$PI . "\n";
我们通过指定类名,范围解析运算符和变量名来访问变量。
PHP final
关键字
最终方法不能被覆盖,最终类不能被扩展。 final
关键字取决于应用的设计。 某些类不应扩展,某些方法不应重写。 此行为由final
关键字强制执行。
finalmethod.php
<?php
class Base {
final function say() {
echo "Base class";
}
}
class Derived extends Base {
function say() {
echo "Derived class";
}
}
该 PHP 脚本无法编译。 我们收到一个错误“无法覆盖最终方法 Base :: say()”。
finalclass.php
<?php
final class Math {
static function getPI() {
return 3.141592;
}
}
class DerivedMath extends Math {
function say() {
echo "DerivedMath class";
}
}
在先前的 PHP 脚本中,我们有一个原型基础Math
类。 该类的唯一目的是为程序员提供一些有用的方法和常量。 (出于简单起见,在我们的例子中,我们只有一种方法。)它不是为了扩展而创建的。 为了防止不知情的其他程序员从此类中派生,创建者创建了final
类。 如果您尝试运行此 PHP 脚本,则会出现以下错误:“致命错误:Class DerivedMath 可能不会从最终类(Math)继承”。
PHP 深拷贝与浅拷贝
数据复制是编程中的重要任务。 对象是 OOP 中的复合数据类型。 对象中的成员字段可以按值或按引用存储。 可以以两种方式执行复制。
浅表副本将所有值和引用复制到新实例中。 引用所指向的数据不会被复制; 仅指针被复制。 新的引用指向原始对象。 对引用成员的任何更改都会影响两个对象。
深拷贝将所有值复制到新实例中。 如果成员存储为引用,则深层副本将对正在引用的数据执行深层副本。 创建引用对象的新副本,并存储指向新创建的对象的指针。 对这些引用对象的任何更改都不会影响该对象的其他副本。 深拷贝是完全复制的对象。
在 PHP 中,我们有一个copy
关键字,默认情况下会执行浅表复制。 它调用对象的__clone()
方法。 我们可以实现创建自定义深层副本的方法。 在 PHP 中,所有对象都是通过引用分配的。
接下来的两个示例对对象执行浅复制和深复制。
shallowcopy.php
<?php
class Object {
public id;
publicsize;
public color;
function __construct(id, size,color) {
this->id =id;
this->size =size;
this->color =color;
}
}
class Color {
public red;
publicgreen;
public blue;
function __construct(red, green,blue) {
this->red =red;
this->green =green;
this->blue =blue;
}
}
color = new Color(23, 42, 223);object1 = new Object(23, "small", color);object2 = clone object1;object2->id++;
object2->color->red = 255;object2->size = "big";
print_r(object1);
print_r(object2);
在上面的 PHP 脚本中,我们定义了两个自定义对象:Object
和Color
。 Object
对象将具有对Color
对象的引用。
$color = new Color(23, 42, 223);
我们创建 Color 对象的实例。
$object1 = new Object(23, "small", $color);
创建对象对象的实例。 它将 Color 对象的实例传递给其构造函数。
$object2 = clone $object1;
我们执行Object
对象的浅表副本。
$object2->id++;
$object2->color->red = 255;
$object2->size = "big";
在这里,我们修改克隆对象的成员字段。 我们增加 id,更改颜色对象的红色部分,并将大小更改为“大”。
print_r(object1);
print_r(object2);
我们使用print_r()
功能比较结果。
$ php shallowcopy.php
Object Object
(
[id] => 23
[size] => small
[color] => Color Object
(
[red] => 255
[green] => 42
[blue] => 223
)
)
Object Object
(
[id] => 24
[size] => big
[color] => Color Object
(
[red] => 255
[green] => 42
[blue] => 223
)
)
我们可以看到 ID 不同:23 与 24。大小不同:“小”与“大”。 但是,这两个实例的颜色对象的红色部分相同:255。更改克隆对象的成员值不会影响原始对象。 更改引用对象的成员也影响了原始对象。 换句话说,两个对象都引用内存中的同一颜色对象。
要更改此行为,我们接下来将做一个深层复制。
deepcopy.php
<?php
class Object {
public id;
publicsize;
public color;
function __construct(id, size,color) {
this->id =id;
this->size =size;
this->color =color;
}
function __clone() {
red =this->color->red;
green =this->color->green;
blue =this->color->blue;
this->color = new Color(red, green,blue);
}
}
class Color {
public red;
publicgreen;
public blue;
function __construct(red, green,blue) {
this->red =red;
this->green =green;
this->blue =blue;
}
}
color = new Color(23, 42, 223);object1 = new Object(23, "small", color);object2 = clone object1;object2->id++;
object2->color->red = 255;object2->size = "big";
print_r(object1);
print_r(object2);
在此 PHP 脚本中,我们实现了__clone()
方法。
function __clone() {
red =this->color->red;
green =this->color->green;
blue =this->color->blue;
this->color = new Color(red, green,blue);
}
在__clone()
方法内部,我们复制红色,绿色和蓝色成员字段并创建一个新的 Color 对象。 现在,$color
字段指向另一个Color
对象。
$ php deepcopy.php
Object Object
(
[id] => 23
[size] => small
[color] => Color Object
(
[red] => 23
[green] => 42
[blue] => 223
)
)
Object Object
(
[id] => 24
[size] => big
[color] => Color Object
(
[red] => 255
[green] => 42
[blue] => 223
)
)
现在,引用的Color
对象的红色部分不相同。 原始对象保留了其先前的 23 值。
PHP 异常
异常是为处理异常的发生而设计的,这些特殊情况会改变程序执行的正常流程。 引发或引发异常并引发异常。
在执行应用期间,许多事情可能出错。 磁盘可能已满,我们无法保存文件。 Internet 连接可能断开,我们的应用尝试连接到站点。 所有这些都可能导致我们的应用崩溃。 为避免发生这种情况,我们必须应对可能发生的所有可能的错误。 为此,我们可以使用异常处理。
从 PHP 5 开始,可以使用例外。大多数 PHP 错误仍然使用旧的错误报告,而不是例外。 使用set_error_handler()
,我们可以解决此问题。
zerodiv.php
<?php
set_error_handler("error_handler");
function error_handler(errno,errstring, errfile,line, trace) {
throw new ErrorException(errstring, errno, 0,errfile, line);
}
try {a = 0;
b = 32;c = b /a;
} catch(ErrorException e) {
echo "Error occurred\n";
echoe->getMessage(), "\n";
}
在上面的 PHP 脚本中,我们有意将数字除以零。 这会导致错误。 该错误也不例外,并且不会被catch
关键字捕获。
set_error_handler("error_handler");
set_error_handler()
功能设置用户定义的错误处理程序功能。
function error_handler(errno,errstring, errfile,line, trace) {
throw new ErrorException(errstring, errno, 0,errfile, $line);
}
在set_error_handler()
函数内部,我们抛出了ErrorException
。 稍后,此异常由catch
关键字捕获。
try {
a = 0;b = 32;
c =b / $a;
}
我们正在检查错误的代码放在try
关键字之后的块中。
} catch(Exception e) {
echoe->getMessage();
}
catch
关键字用于捕获异常。 要了解更多信息,我们在异常对象上调用getMessage()
方法。
$ php zerodiv.php
Error occurred
Division by zero
这是我们的 PHP 脚本的输出。
Exception
是所有异常的基类。 我们可以创建派生自该基类的异常。
myexception.php
<?php
define("LIMIT", 333);
class BigValueException extends Exception {
public function __construct(message) {
parent::__construct(message);
}
}
a = 34325;
try {
if (a > LIMIT) {
throw new BigValueException("Exceeded the maximum value allowed\n");
}
} catch (BigValueException e) {
echoe->getMessage();
}
假设我们处于无法处理大量数字的情况。
define("LIMIT", 333);
大于此常量的数字在我们的 PHP 脚本中被视为“大”。
class BigValueException extends Exception {
我们有一个BigValueException
类。 此类通过extends
关键字从Exception
类派生。
public function __construct(message) {
parent::__construct(message);
}
在构造函数内部,我们称为父级的构造函数。
if ($a > LIMIT) {
throw new BigValueException("Exceeded the maximum value allowed\n");
}
如果该值大于限制,则抛出自定义异常。 我们给异常消息“超出了允许的最大值”。
} catch (BigValueException e) {
echoe->getMessage();
}
我们捕获到异常并将其消息打印到控制台。
PHP 构造函数重载
PHP 不支持 Diret 构造函数重载。 换句话说,每个类只能定义一个构造函数。 许多已经知道 Java 或 C# 语言的程序员都在寻找 PHP 中的类似功能。 我们有两种方法可以处理此问题。
第一种解决方案基于func_get_args()
功能。 第二种解决方案使用工厂模式。
constructors.php
<?php
class Book {
private author = "not specified";
privatetitle = "not specified";
private year = "not specified";
public function __construct() {args = func_get_args();
foreach(["title", "author", "year"] as item) {
if(empty(args)) {
break;
}
this->item = array_shift(args);
}
}
public function __toString() {
return "Author:this->author\nTitle: this->title\nPublished:this->year\n\n";
}
}
book1 = new Book("Stephen Prata", "C Primer Plus");
echobook1;
book2 = new Book("Joshua Bloch", "Effective Java", 2008);
echobook2;
在上面的脚本中,我们有一个Book
类。 我们使用 2 和 3 参数实例化该类。
private author = "not specified";
privatetitle = "not specified";
private $year = "not specified";
我们定义了三个成员字段。 它们的初始值为“未指定”。
$args = func_get_args();
func_get_args()
函数返回一个包含函数的参数列表的数组。 这样的想法是:构造函数中的代码是动态的; 它取决于传递给它的参数。
foreach(["title", "author", "year"] as $item) {
我们使用foreach
关键字浏览所有成员字段。
$this->$item = array_shift($args);
构造函数中最基本的任务之一是初始化类的成员字段。 这是通过上面的代码行完成的。 array_shift()
函数从数组中删除第一项并返回它。
$book1 = new Book("Stephen Prata", "C Primer Plus");
...
$book2 = new Book("Joshua Bloch", "Effective Java", 2008);
我们有两个不同的构造函数。 第一个带有 2 个参数,第二个带有 3 个参数。
$ php constructors.php
Author: C Primer Plus
Title: Stephen Prata
Published: not specified
Author: Effective Java
Title: Joshua Bloch
Published: 2008
这是脚本的结果。
下一个代码示例使用工厂模式模拟构造函数重载。 它是 OOP 中的创建模式之一。 该模式创建对象时未指定要创建的对象的确切类。 更一般地,术语工厂方法通常用于指代主要目的是创建对象的任何方法。
factory.php
<?php
class Cat {
private name = "unspecified";
privateage = "unspecified";
public static function withName(name) {cat = new Cat();
cat->name =name;
return cat;
}
public static function withAge(age) {
cat = new Cat();cat->age = age;
returncat;
}
public static function fullCat(name,age) {
cat = new Cat();cat->name = name;cat->age = age;
returncat;
}
public function __toString() {
return "Name: this->name, Age:this->age\n";
}
}
cici = Cat::withName("Cici");
echocici;
missy = Cat::withAge(6);
echomissy;
lucky = Cat::fullCat("Lucky", 4);
echolucky;
上面的 PHP 脚本中有一个Cat
工厂类。 它具有三种不同的静态功能。 它们每个都返回一个特定的 cat 对象。
private name = "unspecified";
privateage = "unspecified";
我们有两个成员字段。 它们的初始值为“未指定”。
public static function withName(name) {cat = new Cat();
cat->name =name;
return $cat;
}
这是静态的withName()
函数。 此函数创建Cat
类的实例。 它设置name
成员字段并返回对象。
$cici = Cat::withName("Cici");
echo $cici;
我们使用其中一种工厂方法创建猫的实例。 我们回荡对象,即调用该类的__toString()
方法。
$ php factory.php
Name: Cici, Age: unspecified
Name: unspecified, Age: 6
Name: Lucky, Age: 4