注册 登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

Bioinformatics home

 
 
 

日志

 
 

PHP面向对象详解[下]  

2011-07-04 19:32:50|  分类: PHP |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |
11.类的继承
继承作为面向对象的三个重要特性的一个方面,在面向对象的领域有着极其重要的作用,好像
没听说哪个面向对象的语言不支持继承。继承是PHP5 面向对象程序设计的重要特性之一,它是指
建立一个新的派生类,从一个或多个先前定义的类中继承数据和函数,而且可以重新定义或加进新
数据和函数,从而建立了类的层次或等级。说的简单点就是,继承性是子类自动共享父类的数据结
构和方法的机制,这是类之间的一种关系。在定义和实现一个类的时候,可以在一个已经存在的类
的基础之上来进行,把这个已经存在的类所定义的内容作为自己的内容,并加入若干新的内容。比
如你现在已经有一个“人”这个类了,这个类里面有两个成员属性“姓名和年龄”以及还有两个成
员方法“说话的方法和走路的方法”,如果现在程序需要一个学生的类, 因为学生的也是人,所以
学生也有成员属性“姓名和年龄”以及成员方法“说话的方法和走路的方法”,这个时候你就可以
让学生类来继承人这个类,继承之后,学生类就会把人类里面的所有的属性都继承过来,就不用你
再去重新声明一遍这些成员属性和方法了,因为学生类里面还有所在学校的属性和学习的方法,所
以在你做的学生类里面有继承自人类里面的属性和方法之外在加上学生特有的“所在学校属性”和
“学习的方法”, 这样一个学生类就声明完成了,继承我们也可以叫做“扩展”,从上面我们就可
以看出,学生类对人类进行了扩展, 在人类里原有两个属性和两个方法的基础上加上一个属性和
一个方法扩展出来一个新的学生类。
通过继承机制,可以利用已有的数据类型来定义新的数据类型。所定义的新的数据类型不仅拥
有新定义的成员,而且还同时拥有旧的成员。我们称已存在的用来派生新类的类为基类,又称为父
类以及超类。由已存在的类派生出的新类称为派生类,又称为子类。

在软件开发中,类的继承性使所建立的软件具有开放性、可扩充性,这是信息组织与分类的行
之有效的方法,它简化了对象、类的创建工作量,增加了代码的可重性。采用继承性,提供了类的
规范的等级结构。通过类的继承关系,使公共的特性能够共享,提高了软件的重用性。
在C++语言中,一个派生类可以从一个基类派生,也可以从多个基类派生。从一个基类派生的
继承称为单继承;从多个基类派生的继承称为多继承。
但是在PHP 和Java 语言里面没有多继承,只有单继承,也就是说,一个类只能直接从一个类
中继承数据,这就是我们所说的单继承。
例如:
下面是“人”类的抽象
代码片断
//定义一个“人”类作为父类
class Person
{
//下面是人的成员属性
var $name; //人的名字
var $sex; //人的性别
var $age; //人的年龄
//定义一个构造方法参数为属性姓名$name、性别$sex 和年龄$age 进行赋值
function __construct($name, $sex, $age)
{
$this->name=$name;
$this->sex=$sex;
$this->age=$age;
}
//这个人可以说话的方法, 说出自己的属性
function say()
{
echo "我的名字叫:".$this->name." 性别:".$this->sex." 我的年龄是:".$this->age."<br>";
}
}
下面我们做一个“学生类”,如果不是用继承如下:
代码片断
//定义一个“人”类做为父类
class Student
{
//下面是人的成员属性
var $name; //人的名字
var $sex; //人的性别
var $age; //人的年龄
var $school; //学生所在学校的属性
//定义一个构造方法参数为属性姓名$name、性别$sex 和年龄$age 进行赋值
function __construct($name=””, $sex=””, $age=””, $school=””)
{
$this->name=$name;
$this->sex=$sex;
$this->age=$age;
$this->school=$school;
}
//这个人可以说话的方法, 说出自己的属性
unction say()
{
echo "我的名字叫:".$this->name." 性别:".$this->sex." 我的年龄是:".$this->age."<br>";
}
//这个学生学习的方法
function study()
{
echo "我的名字叫:".$this->name." 我正在”.$this->school.”学习<br>";
}
}
//定义一个子类“学生类“使用”extends”关键字来继承”人”类
class Student extends Person
{
var $school; //学生所在学校的属性
//这个学生学习的方法
function study()
{
echo "我的名字叫:".$this->name." 我正在”.$this->school.”学习<br>";
}
}
通过上面“Student”类的定义,Student 类通过使用“extends”这个关键字把Person 类里的
所有成员属性和成员方法都继承过来了,并扩展了一个所在学校成员属性“school”,和一个学习
方法“study()”。现在子类“Student”里面和使用这个类实例出来的对象都具有如下的属性和方
法:
学生类“Student”里面的成员属性有:
姓名:name;
年龄:age;
性别:sex;
学校:school;
学生类“Student”里面的成员方法有:
说话方法:say();
学习方法:study();
通过上面类继承的使用简化了对象、类的创建工作量,增加了代码的可重性。但是从上面这一
个例子上中“可重用性”以及其它的继承性所带来的影响,我们看的还不是特别的明显,你扩展的
去想一下,人有无数个岗位,比如上面的学生还有老师、工程师、医生、工人等,很多很多,如果
每个类都定义“人”都共同具有的属性和方法,想一想会有很大的工作量,这些属性和方法都可以
从“Person”人类里面继承过来。
12.重载新的方法
在学习PHP 这种语言中你会发现,PHP 中的方法是不能重载的,所谓的方法重载就是定义相同
的方法名,通过“参数的个数”不同或“参数的类型”不同,来访问我们的相同方法名的不同方法。
但是因为PHP 是弱类型的语言,所以在方法的参数中本身就可以接收不同类型的数据,又因为PHP
的方法可以接收不定个数的参数,所以通过传递不同个数的参数调用不相同方法名的不同方法也是
不成立的。所以在PHP 里面没有方法重载。不能重载也就是在你的项目中不能定义相同方法名的方
法。另外,因为PHP 没有名字空间的概念,在同一个页面和被包含的页面中不能定义相同名称的方
法,也不能定义和PHP 给我提供的方法重名,当然在同一个类中也不能定义相同名称的方法。
我们这里所指的重载新的方法所指的是什么呢?其实我们所说的重载新的方法就是子类覆盖
父类的已有的方法,那为什么要这么做呢?父类的方法不是可以继承过来直接用吗?但有一些情况
是我们必须要覆盖的,比如说我们前面提到过的例子里面,“Person”这个人类里面有一个“说话”
的方法,所有继承“Person”类的子类都是可以“说话”的, 我们“Student”类就是“Person”类
的子类,所以“Student”的实例就可以“说话”了,但是人类里面“说话”的方法里面说出的是“Person”
类里面的属性,而“Student”类对“Person”类进行了扩展,又扩展出了几个新的属性,如果使用
继承过来的“say()”说话方法的话,只能说出从“Person”类继承过来的那些属性,那么新扩展的
那些属性使用这个继承过来的“say()”的方法就说不出来了,那有的人就问了,我在“Student”这
个子类中再定义一个新的方法用于说话,说出子类里面所有的属性不就行了吗?一定不要这么做,
从抽象的角度来讲, 一个“学生”不能有两种“说话”的方法,就算你定义了两个不同的说话的
方法,可以实现你想要的功能,被继承过来的那个“说话”方法可能没有机会用到了,而且是继承
过来的你也删不掉。这个时候我们就要用到覆盖了。
虽然说在PHP 里面不能定义同名的方法, 但是在父子关系的两个类中,我们可以在子类中定
义和父类同名的方法,这样就把父类中继承过来的方法覆盖掉了。
代码片断
<?
//定义一个“人”类做为父类
class Person
{
//下面是人的成员属性
var $name; //人的名字
var $sex; //人的性别
var $age; //人的年龄
//定义一个构造方法参数为属性姓名$name、性别$sex 和年龄$age 进行赋值
function __construct($name, $sex, $age)
{
$this->name=$name;
$this->sex=$sex;
$this->age=$age;
}
//这个人可以说话的方法, 说出自己的属性
function say()
{
echo "我的名字叫:".$this->name." 性别:".$this->sex." 我的年龄是:".$this->age."<br>";
}
}
class Student extends Person
{
var $school; //学生所在学校的属性
//这个学生学习的方法
function study()
{
echo "我的名字叫:".$this->name." 我正在”.$this->school.”学习<br>";
}
//这个学性可以说话的方法, 说出自己所有的属性,覆盖了父类的同名方法
function say()
{
echo "我的名字叫:".$this->name." 性别:".$this->sex." 我的年龄是:".$this->age."
我在".$this->school."上学.<br>";
}
}?
>
上面的例子, 我们就在“Student”子类里覆盖了继承父类里面的“say()”的方法,通过覆盖
我们就实现了对“方法”扩展。
但是,像这样做虽然解决了我们上面说的问题,但是在实际开发中,一个方法不可能就一条代
码或是几条代码,比如说“Person”类里面的“say()”方法有里面有100 条代码,如果我们想对
这个方法覆盖保留原有的功能外加上一点点功能,就要把原有的100 条代码重写一次, 再加上扩
展的几条代码,这还算是好的,而有的情况,父类中的方法是看不见原代码的,这个时候你怎么去
重写原有的代码呢?我们也有解决的办法,就是在子类的这个方法中可以调用到父类中被覆盖的方
法, 也就是把被覆盖的方法原有的功能拿过来再加上自己的一点功能,可以通过两种方法实现在
子类的方法中调用父类被覆盖的方法:
一种是使用父类的“类名::”来调用父类中被覆盖的方法;
一种是使用“parent::”的方试来调用父类中被覆盖的方法;
代码片断
<?php
class Student extends Person
{
var $school; //学生所在学校的属性
//这个学生学习的方法
function study()
{
echo "我的名字叫:".$this->name." 我正在”.$this->school.”学习<br>";
}
//这个学性可以说话的方法, 说出自己所有的属性,覆盖了父类的同名方法
function say()
{
//使用父类的“类名::“来调用父类中被覆盖的方法;
// Person::say();
//或者使用“parent::”的方试来调用父类中被覆盖的方法;
parent::say();
//加上一点自己的功能
echo “我的年龄是:".$this->age."我在".$this->school."上学.<br>";
}
}?
>
现在用两种方式都可以访问到父类中被覆盖的方法,我们选那种方式最好呢?用户可能会发现
自己写的代码访问了父类的变量和函数。如果子类非常精炼或者父类非常专业化的时候尤其是这
样。不要用代码中父类文字上的名字,应该用特殊的名字parent,它指的就是子类在extends 声
明中所指的父类的名字。这样做可以避免在多个地方使用父类的名字。如果继承树在实现的过程中
要修改,只要简单地修改类中extends 声明的部分。
同样,构造方法在子类中如果没有声明的话,也可以使用父类中的构造方法,如果子类中重新
定义了一个构造方法也会覆盖掉父类中的构造方法,如果想使用新的构造方法为所有属性赋值也可
以用同样的方式。
代码片断
class Student extends Person
{
var $school; //学生所在学校的属性
//这个学生学习的方法
function study()
{
echo "我的名字叫:".$this->name." 我正在”.$this->school.”学习<br>";
}
//这个学性可以说话的方法, 说出自己所有的属性,覆盖了父类的同名方法
function say()
{
//使用父类的“类名::“来调用父类中被覆盖的方法;
// Person::say();
//或者使用“parent::”的方试来调用父类中被覆盖的方法;
parent::say();
//加上一点自己的功能
echo “我的年龄是:".$this->age."我在".$this->school."上学.<br>";
}
}
13.访问类型
类型的访问修饰符允许开发人员对类成员的访问进行限制,这是PHP5 的新特性,但却是OOP
语言的一个好的特性。而且大多数OOP 语言都已支持此特性。PHP5 支持如下3 种访问修饰符:
public(公有的、默认的),private(私有的)和protected(受保护的)三种。
public 公有修饰符,类中的成员将没有访问限制,所有的外部成员都可以访问(读和写)这
个类成员(包括成员属性和成员方法),在PHP5 之前的所有版本中,PHP 中类的成员都是public 的,
而且在PHP5 中如果类的成员没有指定成员访问修饰符,将被视为public 。
例:public $name;
public function say(){};
private 私有修改符,被定义为private 的成员,对于同一个类里的所有成员是可见的,即是
没有访问限制;但对于该类的外部代码是不允许改变甚至读操作,对于该类的子类,也不能访问
private 修饰的成员。
例: private $var1 = ‘A’; //属性
private function getValue(){} //函数
protected 保护成员修饰符,被修饰为protected 的成员不能被该类的外部代码访问。但是对
于该类的子类有访问权限,可以进行属性、方法的读及写操作,该子类的外部代码包括其的子类都
不具有访问其属性和方法的权限。
例:protected $name;
protected function say(){};
private protected public
同一个类中√ √ √
类的子类中√ √
所有的外部成员√
片断
<?php
/**
* Define MyClass
*/
class MyClass
{
public $public = 'Public';
protected $protected = 'Protected';
private $private = 'Private';
function printHello()
{
echo $this->public;
echo $this->protected;
echo $this->private;
}
}
$obj = new MyClass();
echo $obj->public; //Works
echo $obj->protected; // Fatal Error
echo $obj->private; // Fatal Error
$obj->printHello(); // Shows Public, Protected and Private
/**
* Define MyClass2
*/
class MyClass2 extends MyClass
{
//We can redeclare the public and protected method, but not private
protected $protected = 'Protected2';
function printHello()
{
echo $this->public;
echo $this->protected;
echo $this->private;
}
}
$obj2 = new MyClass2();
echo $obj->public; //Works
echo $obj2->private; // Undefined
echo $obj2->protected; // Fatal Error
$obj2->printHello(); // Shows Public, Protected2, not Private
?>
<?php
/**
* Define MyClass
*/
class MyClass
{
// Contructors must be public
public function __construct() { }
// Declare a public method
public function MyPublic() { }
// Declare a protected method
protected function MyProtected() { }
// Declare a private method
private function MyPrivate() { }
// This is public
function Foo()
{
$this->MyPublic();
$this->MyProtected();
$this->MyPrivate();
}
}
$myclass = new MyClass;
$myclass->MyPublic(); //Works
$myclass->MyProtected(); // Fatal Error
$myclass->MyPrivate(); // Fatal Error
$myclass->Foo(); // Public, Protected and Private work
/**
* Define MyClass2
*/
class MyClass2 extends MyClass
{
// This is public
function Foo2()
{
$this->MyPublic();
$this->MyProtected();
$this->MyPrivate(); // Fatal Error
}
}
$myclass2 = new MyClass2;
$myclass2->MyPublic(); //Works
$myclass2->Foo2(); // Public and Protected work, not Private
?>
14.final 关键字的应用
这个关键字只能用来定义类和定义方法,不能使用final 这个关键字来定义成员属性,因为
final 是常量的意思,我们在PHP 里定义常量使用的是define()函数,
所以不能使用final 来定义
成员属性。
使用final 关键标记的类不能被继承;
代码片断
final class Person
{
… …
}
class Student extends Person
{}
会出现下面错误:
Fatal error: Class Student may not inherit from final class (Person)
使用final 关键标记的方法不能被子类覆盖,是最终版本;
代码片断
class Person
{
final function say()
{
}
}
class Student extends Person
{
function say()
{
}
}
会出现下面错误:
Fatal error: Cannot override final method Person::say()
15.static 和const 关键字的使用
Static 关键字是在类中描述成员属性和成员方法是静态的;静态的成员好处在那里呢?前面我
们声明了“Person”的人类,在“Person”这个类里如果我们加上一个“人所属国家”的属性,这
样用“Person”这个类实例化出几百个或者更多个实例对象,每个对象里面就都有“所属国家”的
属性了,如果开发的项目就是为中国人而开发的,那么每个对象里面就都有一个国家的属性是“中
国”其它的属性是不同的,如果我们把“国家”的属性做成静态的成员,这样国家的属性在内存中
就只有一个,而让这几百个或更多的对象共用这一个属性,static 成员能够限制外部的访问,因为
static 的成员是属于类的,是不属于任何对象实例,是在类第一次被加载的时候分配的空间,其他
类是无法访问的,只对类的实例共享,能一定程度对类该成员形成保护。
从内存的角度我们来分析一下,内存从逻辑上被分为四段,其中对象是放在“堆内存”里面,
对象的引用被放到了“栈内存”里,而静态成员则放到了“初始化静态段”,在类第一次被加载的
时候放入的,可以让堆内存里面的每个对象所共享,如下图;
类的静态变量,非常类似全局变量,能够被所有类的实例共享,类的静态方法也是一样的,类
似于全局函数。
代码片断
<?
class Person
{
//下面是人的静态成员属性
public static $myCountry="中国";
// var $name; //人的名字
//这是人的静态成员方法
public static function say()
{
echo "我是中国人<br>";
}
}
//输出静态属性
echo Person::$myCountry;
//访问静态方法
Person::say();
//重新给静态属性赋值
Person::$myCountry="美国";
echo Person::$myCountry;
?>
因为静态成员是在类第一次加载的时候就创建的,所以在类的外部不需要对象而使用类名就可
以访问的到静态的成员;上面说过,静态成员被这个类的每个实例对象所共享,那么我们使用对象
可不可以访问类中的静态成员呢?从上图中我们可以看到,静态的成员不是在每个对象内部存在
的,但是每个对象都可以共享,所以我们如果使用对象访问成员的话就会出现没有这个属性定义,
使用对象访问不到静态成员的,在其它的面向对象的语言中,比如Java 是可以使用对象的方式访
问静态成员的,如果PHP 中可以使用对象访问静态成员的话,我们也尽量不要去使用,因为静态的
成员我们在做项目的时候目的就是使用类名去访问。
类里面的静态方法只能访问类的静态的属性,在类里面的静态方法是不能访问类的非静态成员
的,原因很简单,我们要想在本类的方法中访问本类的其它成员,我们需要使用$this 这个引用,
而$this 这个引用指针是代表调用此方法的对象,我们说了静态的方法是不用对象调用的,而是使
用类名来访问,所以根本就没有对象存在,也就没有$this 这个引用了,没有了$this 这个引用就
不能访问类里面的非静态成员,又因为类里面的静态成员是可以不用对象来访问的,所以类里面的
静态方法只能访问类的静态的属性,既然$this 不存在,在静态方法中访其它静态成员我们使用的
是一个特殊的类“self”;self 和$this 相似,只不过self 是代表这个静态方法所在的类。所以
在静态方法里,可以使用这个方法所在的类的“类名”,也可以使用“self”来访问其它静态成员,
如果没有特殊情况的话,我们通常使用后者,即“self::成员属性”的方式。
代码片断
<?
class Person
{
//下面是人的静态成员属性
public static $myCountry="中国";
//这是人的静态成员方法, 通过self访问其它静态成员
public static function say()
{
echo "我是".self::$myCountry."<br>";
}
}
//访问静态方法
Person::say();
?>
在非静态方法里可不可以访问静态成员呢,当然也是可以的了,但是也不能使用“$this”引用
也要使用类名或是“self::成员属性的形式”。
const 是一个定义常量的关键字,在PHP 中定义常量使用的是“define()”这个函数,但是在
类里面定义常量使用的是“const”这个关键字,类似于C 中的#define 如果在程序中改变了它的值,
那么会出现错误,用“const”修饰的成员属性的访问方式和“static”修饰的成员访问的方式差
不多,也是使用“类名”,在方法里面使用“self”关键字。但是不用使用“$”符号,也不能使
用对象来访问。
代码片断
<?php
class MyClass
{
//定义一个常量constant
const constant = 'constant value';
function showConstant() {
echo self::constant . "\n"; //使用self 访问,不要加”$”
}
}
echo MyClass::constant . "\n"; //使用类名来访问,也不加”$”
$class = new MyClass();
$class->showConstant();
// echo $class::constant; 是不允许的
?>
16.__toString()方法
我们前面说过在类里面声明“--”开始的方法名的方法(PHP 给我们提供的),都是在某一时刻
不同情况下自动调用执行的方法,“__toString()”方法也是一样自动被调用的,是在直接输出对
象引用时自动调用的, 前面我们讲过对象引用是一个指针,比如说:“$p=new Person()”中,$p
就是一个引用,我们不能使用echo 直接输出$p,这样会输出“Catchable fatal error: Object of
class Person could not be converted to string ” 这样的错误, 如果你在类里面定义了
“ __toString()” 方法, 在直接输出对象引用的时候, 就不会产生错误, 而是自动调用了
“__toString()”方法,输出“__toString()”方法中返回的字符,所以“__toString()”方法一
定要有个返回值(return 语句).
代码片断
<?php
// Declare a simple class
class TestClass
{
public $foo;
public function __construct($foo) {
$this->foo = $foo;
}
//定义一个__toString方法,返加一个成员属性$foo
public function __toString() {
return $this->foo;
}
}
$class = new TestClass('Hello');
//直接输出对象
echo $class;
?>
上例输出:Hello
17.克隆对象
有的时候我们需要在一个项目里面,使用两个或多个一样的对象,如果你使用“new”关键字
重新创建对象的话,再赋值上相同的属性,这样做比较烦琐而且也容易出错,所以要根据一个对象
完全克隆出一个一模一样的对象,是非常有必要的,而且克隆以后,两个对象互不干扰。
在PHP5 中我们使用“clone”这个关键字克隆对象;
代码片断
<?
class Person
{
//下面是人的成员属性
var $name; //人的名字
var $sex; //人的性别
var $age; //人的年龄
//定义一个构造方法参数为属性姓名$name、性别$sex 和年龄$age 进行赋值
function __construct($name="", $sex="", $age="")
{
$this->name=$name;
$this->sex=$sex;
$this->age=$age;
}
//这个人可以说话的方法, 说出自己的属性
function say()
{
echo "我的名字叫:".$this->name." 性别:".$this->sex." 我的年龄是:".$this->age."<br>";
}
}
$p1=new Person("张三", "男", 20);
//使用“clone”克隆新对象p2,和p1 对象具有相同的属性和方法。
$p2=clone $p1;
$p2->say();
?>
PHP5 定义了一个特殊的方法名“__clone()”方法,是在对象克隆时自动调用的方法,用
“__clone()”方法将建立一个与原对象拥有相同属性和方法的对象,如果想在克隆后改变原对象
的内容,需要在__clone()中重写原本的属性和方法,“__clone()”方法可以没有参数,它自动包
含$this 和$that 两个指针,$this 指向复本,而$that 指向原本;
代码片断
<?php
class Person
{
//下面是人的成员属性
var $name; //人的名字
var $sex; //人的性别
var $age; //人的年龄
//定义一个构造方法参数为属性姓名$name、性别$sex 和年龄$age 进行赋值
function __construct($name="", $sex="", $age="")
{
$this->name=$name;
$this->sex=$sex;
$this->age=$age;
}
//这个人可以说话的方法, 说出自己的属性
function say()
{
echo "我的名字叫:".$this->name." 性别:".$this->sex." 我的年龄是:".$this->age."<br>";
}
//对象克隆时自动调用的方法, 如果想在克隆后改变原对象的内容,需要在__clone()
中重写原本的属性和方法
function __clone()
{
//$this 指的复本p2, 而$that 是指向原本p1,这样就在本方法里,改变了复本的属性。
$this->name="我是假的$that->name";
$this->age=30;
}
}
$p1=new Person("张三", "男", 20);
$p2=clone $p1;
$p1->say();
$p2->say();
?>
上例输出:
执行结果
我的名字叫:张三性别:男我的年龄是:20
我的名字叫:我是假的张三性别:男我的年龄是:30
18.__call 处理调用错误
在程序开发中,如果在使用对象调用对象内部方法时候,调用的这个方法不存在那么程序就会
出错,然后程序退出不能继续执行。那么可不可以在程序调用对象内部不存在的方法时,提示我们
调用的方法及使用的参数不存在,但程序还可以继续执行,这个时候我们就要使用在调用不存在的
方法时自动调用的方法“__call()”。
代码片断
<?php
//这是一个测试的类,里面没有属性和方法
class Test
{
}
//产生一个Test 类的对象
$test=new Test();
//调用对象里不存在的方法
$test->demo("one", "two", "three");
//程序不会执行到这里
echo "this is a test<br>";
?>
上例出现如下错误,程序通出不能继续执行;
Fatal error: Call to undefined method Test::demo()
下面我们加上”__call()”方法,这个方法有2 个参数,第一个参数为调用不存在的方法过程中,
自动调用__call()方法时,把这个不存在的方法名传给第一个参数,第二个参数则是把这个方法的多
个参数以数组的形式传进来。
代码片断
<?php
//这是一个测试的类,里面没有属性和方法
class Test
{
//调用不存的方法时自动调用的方法,第一个参数为方法名,第二个参数是数组参数
function __call($function_name, $args)
{
print "你所调用的函数:$function_name(参数:";
print_r($args);
print ")不存在!<br>\n";
}
}
//产生一个Test类的对象
$test=new Test();
//调用对象里不存在的方法
$test->demo("one", "two", "three");
//程序不会退出可以执行到这里
echo "this is a test<br>";
?>
上例输出结果为:
执行结果
你所调用的函数:demo(参数:Array ( [0] => one [1] => two [2] => three ) )不存在!
this is a test.
19.抽象方法和抽象类
在OOP 语言中,一个类可以有一个或多个子类,而每个类都有至少一个公有方法做为外部代码
访问其的接口。而抽象方法就是为了方便继承而引入的,我们先来看一下抽象类和抽象方法的定义
再说明它的用途。
什么是抽象方法?我们在类里面定义的没有方法体的方法就是抽象方法,所谓的没有方法体指
的是,在方法声明的时候没有大括号以及其中的内容,而是直接在声明时在方法名后加上分号结束,
另外在声明抽象方法时还要加一个关键字“abstract”来修饰;
例如:
abstract function fun1();
abstract function fun2();
上例是就是“abstract”修饰的没有方法体的抽象方法“fun1()”和“fun2()”,不要忘记抽象
方法后面还要有一个分号;那么什么是抽象类呢?只要一个类里面有一个方法是抽象方法,那么这
个类就要定义为抽象类,抽象类也要使用“abstract”关键字来修饰;在抽象类里面可以有不是抽
象的方法和成员属性,但只要有一个方法是抽象的方法,这个类就必须声明为抽象类,使用
“abstract”来修饰。
例如:
代码片断
abstract class Demo
{
var $test;
abstract function fun1();
abstract function fun2();
function fun3()
{
… .
}
}
上例中定义了一个抽象类“Demo”使用了“abstract”来修饰, 在这个类里面定义了一个成员
属性“$test”,和两个抽象方法“fun1”和“fun2”还有一个非抽象的方法fun3();那么抽象类我
们怎么使用呢?最重要的一点就是抽象类不能产生实例对象,所以也不能直接使用,前面我们多次
提到过类不能直接使用,我们使用的是通过类实例化出来的对象,那么抽象类不能产生实例对象我
们声明抽象类有什么用呢?我们是将抽象方法是作为子类重载的模板使用的,定义抽象类就相当于
定义了一种规范,这种规范要求子类去遵守,子类继承抽象类之后,把抽象类里面的抽象方法按照
子类的需要实现。子类必须把父类中的抽象方法全部都实现,否则子类中还存在抽象方法,那么子
类还是抽象类,还是不能实例化对;为什么我们非要从抽象类中继承呢?因为有的时候我们要实现
一些功能就必须从抽象类中继承,否则这些功能你就实现不了,如果继承了抽象类,就要实现类其
中的抽象方法;
代码片断
<?
abstract class Demo
{
var $test;
abstract function fun1();
abstract function fun2();
function fun3()
{
… .
}
}
$demo=new Demo(); //抽象类为能产生实例对象,所以这样做是错的,实例化对象交给子类
class Test extends Demo
{
function fun1()
{

} function fun2()
{

}
}
$test=new Test(); //子类可以实例化对象,因为实现了父类中所有抽象方法
?>
20. php5 接口技术
PHP 与大多数面向对象编程语言一样,不支持多重继承.也就是说每个类只能继承一个父类.为
了解决这个问题,PHP 引入了接口,接口的思想是指定了一个实现了该接口的类必须实现的一系列
方法。接口是一种特殊的抽象类,抽象类又是一种特殊的类,所以接口也是一种特殊的类,为什么
说接口是一种特殊的抽象类呢?如果一个抽象类里面的所有的方法都是抽象方法,那么我们就换一
种声明方法使用“接口”;也就是说接口里面所有的方法必须都是声明为抽象方法,另外接口里面
不能声明变量,而且接口里面所有的成员都是public 权限的。所以子类在实现的时候也一定要使
用public 权限实限。
声明一个类的时候我们使用的关键字是“class”,而接口一种特殊的类,使用的关键字是
“interface”;
类的定义:class 类名{ … },接口的声明:interface 接口名{ … }
代码片断
<?php
//定义一个接口使用interface 关键字,“One”为接口名称
interface One
{
//定义一个常量
const constant = 'constant value';
//定义了一个抽象方法”fun1”
public function fun1();
//定义了抽象方法”fun2”
public function fun2();
}
?>
上例中定义了一个接口“one”,里面声明了两个抽象方法“fun1”和“fun2”,因为接口里
面所有的方法都是抽象方法,所以在声明抽象方法的时候就不用像抽象类那样使用“abstract”这
个关键字了,默认的已经加上这个关键字,另外在接口里边的“public”这个访问权限也可以去掉,
因为默认就是public 的,因为接口里所有成员都要是公有的,所在对于接口里面的成员我们就不
能使用“private”的和“protected”的权限了,都要用public 或是默认的。另外在接口里面我
们也声明了一个常量“constant”,因为在接口里面不能用变量成员,所以我们要使用const 这个
关键字声明。
因为接口是一种特殊的抽象类,里面所有的方法都是抽象方法,所以接口也不能产生实例对象;
它也作为一种规范,所有抽象方法需要子类去实现。
我们可以使用“extends”关键字让一个接口去继承另一个接口;
代码片断
<?php
//使用”extends”继承另外一个接口
interface Two extends One
{
function fun3();
function fun4();
}
?>
而我们定义一个接口的子类去实现接口中全部抽象方法使用的关键字是“implements”,而不
是我们前面所说的“extends”;
代码片断
<?php
//使用“implements”这个关键字去实现接口中的抽象方法
class Three implements One
{
function fun1()
{
… .
} function fun2()
{
… .
}
}
//实现了全部方法,我们去可以使用子类去实例化对象了
$three=new Three();
?>
我们也可以使用抽象类,去实现接口中的部分抽象方法,但要想实例化对象,这个抽象类还要
有子类把它所有的抽象方法都实现才行;
在前面我们说过,PHP 是单继承的,一个类只能有一父类,但是一个类可以实现多个接口,就
相当于一个类要遵守多个规范,就像我们不仅要遵守国家的法律,如果是在学校的话,还要遵守学
校的校规一样;
代码片断
<?php
//使用implements实现多个接口
class Four implemtns 接口一, 接口二, … .
{
//必须把所有接口中的方法都要实现才可以实例化对象。
}
?>
PHP 中不仅一个类可以实现多个接口,也可以在继承一个类的同时实现多个接口, 一定要先
继承类再去实现接口;
代码片断
<?php
//使用extends 继承一个类,使用implements 实现多个接口
class Four extends 类名一implemtns 接口一, 接口二, … .
{
//所有接口中的方法都要实现才可以实例化对象
… … … ..
}?
>
21.多态的应用
多态是除封装和继承之外的另一个面向对象的三大特性之一,我个人看来PHP 中虽然可以实现
多态,但和C++还有Java 这些面向对象的语言相比,多态性并不是那么突出,因为PHP 本身就是一
种弱类型的语言,不存在父类对象转化为子类对象或者是子类对象转化为父类对象的问题,所以多
态的应用并不是那么的明显;所谓多态性是指一段程序能够处理多种类型对象的能力,比如说在公
司上班,每个月财务发放工资,同一个发工资的方法,在公司内不同的员工或是不同职位的员工,
都是通过这个方法发放的,但是所发的工资都是不相同的。所以同一个发工资的方法就出现了多种
形态。对于面向对象的程序来说,多态就是把子类对象赋值给父类引用,然后调用父类的方法,去
执行子类覆盖父类的那个方法,但在PHP 里是弱类型的,对象引用都是一样的不分父类引用,还是
子类引用。
我们现在来看一个例子,首先还是要使用多态就要有父类对象和子类对象的关系。做一个形状
的接口或是抽象类作为父类,里面有两个抽象方法,一个求周长的方法,另一个是求面积的方法;
这接口的子类是多种不同的形状,每个形状又都有周长和面积,又因为父类是一个接口,所以子类
里面就必须要实现父类的这两个周长和面积的抽象方法,这样做的目的是每种不同形状的子类都遵
守父类接口的规范,都要有求周长和求面积的方法。
代码片断
<?
//定义了一个形状的接口,里面有两个抽象方法让子类去实现
interface Shape
{
function area();
function perimeter();
}
//定义了一个矩形子类实现了形状接口中的周长和面积
class Rect implements Shape
{
private $width;
private $height;
function __construct($width, $height)
{
$this->width=$width;
$this->height=$height;
} function area()
{
return "矩形的面积是:".($this->width*$this->height);
} function perimeter()
{
return "矩形的周长是:".(2*($this->width+$this->height));
}
}
//定义了一个圆形子类实现了形状接口中的周长和面积
class Circular implements Shape
{
private $radius;
function __construct($radius)
{
$this->radius=$radius;
} function area()
{
return "圆形的面积是:".(3.14*$this->radius*$this->radius);
} function perimeter()
{
return "圆形的周长是:".(2*3.14*$this->radius);
}
}
//把子类矩形对象赋给形状的一个引用
$shape=new Rect(5, 10);
echo $shape->area()."<br>";
echo $shape->perimeter()."<br>";
//把子类圆形对象赋给形状的一个引用
$shape=new Circular(10);
echo $shape->area()."<br>";
echo $shape->perimeter()."<br>";
?>
上例执行结果:
执行结果
矩形的面积是:50
矩形的周长是:30
圆形的面积是:314
圆形的周长是:62.8
通过上例我们看到,把矩形对象和圆形对象分别赋给了变量$shape,调用$shape 引用中的面积
和周长的方法,出现了不同的结果,这就是一种多态的应用,其实在我们PHP 这种弱类形的面向对
象的语言里面,多态的特性并不是特别的明显,其实就是对象类型变量的变项应用。
22.把对象串行化
有时候需要把一个对象在网络上传输,为了方便传输,可以把整个对象转化为二进制串,等到
达另一端时,再还原为原来的对象,这个过程称之为串行化, 就像我们现在想把一辆汽车通过轮
船运到美国去,因为汽车的体积比较大,我们可以把汽车拆开成小的部件,然后我们把这些部件通
过轮船运到美国去,到了美国再把这些部件组装回汽车。
有两种情况我们必须把对象串行化,第一种情况就是把一个对象在网络中传输的时候要将对象
串行化,第二种情况就是把对象写入文件或是数据库的时候用到串行化。
串行化有两个过程,一个是串行化,就是把对象转化为二进制的字符串,我们使用serialize()
函数来串行化一个对象,另一个是反串行化,就是把对象转化的二进制字符串再转化为对象, 我
们使用unserialize()函数来反串行化一个对象.
PHP 中serialize()函数的参数为对象名,返回值为一个字符串,Serialize()返回的字符串含
义模糊,一般我们不会解析这个串来得到对象的信息,我们只要把返回来的这个字符串传到网络另
一端或是保存到方件中即可。
PHP 中unserialize()函数来反串
-EOF
  评论这张
 
阅读(933)| 评论(0)
推荐 转载

历史上的今天

在LOFTER的更多文章

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2017