php面象對象知識總結
1.面向對象的概念
2.什么是類,什么是對象,類和對象之間的關系
3.什么是面向對象編程呢?
4.如何抽象出一個類?
5.如何實例化對象?
6.如何去使用對象中的成員?
7.特殊的引用“$this”的使用
8.構造方法與析構方法
9.封裝性
10.__set()、 __get()、 __isset()、 __unset()四個方法的應用
11.類的繼承
12.多態的應用
13.重載新的方法
14.訪問類型
15.靜態成員的使用
16.final 關鍵字的應用
17.__toString()方法
18.克隆對象
19.__call 處理調用錯誤
20.自動加載類
21.把對象串行化
22.抽象方法和抽象類
23.php5 接口技術
1.面向對象的概念
面向對象編程(Object Oriented Programming, OOP, 面向對象程序設計)是一種計算機編程
架構,OOP 的一條基本原則是計算機程序是由單個能夠起到子程序作用的單元或對象組合而成,OOP
達到了軟件工程的三個目標:重用性、靈活性和擴展性。為了實現整體運算,每個對象都能夠接收
信息、處理數據和向其它對象發送信息。面向對象一直是軟件開發領域內比較熱門的話題,首先,
面向對象符合人類看待事物的一般規律。其次,采用面向對象方法可以使系統各部分各司其職、各
盡所能。為編程人員敞開了一扇大門,使其編程的代碼更簡潔、更易于維護,并且具有更強的可重
用性。有人說 PHP 不是一個真正的面向對象的語言,這是事實。PHP 是一個混合型語言,你可以使
用 OOP,也可以使用傳統的過程化編程。然而,對于大型項目,你可能需要在 PHP 中使用純的 OOP
去聲明類,而且在你的項目里只用對象和類。這個概念我先不多說了,因為有很多朋友遠離面向對
象編程的主要原因就是一接觸面向對象概念的時候就理解不上去, 所以就不想去學下去了。等讀
者看完整篇內容后再去把概念搞明白吧。
2.什么是類,什么是對象,類和對象之間的關系
類的概念:類是具有相同屬性和服務的一組對象的集合。它為屬于該類的所有對象提供了統一
的抽象描述,其內部包括屬性和服務兩個主要部分。在面向對象的編程語言中,類是一個獨立的程
序單位,它應該有一個類名并包括屬性說明和服務說明兩個主要部分。
對象的概念:對象是系統中用來描述客觀事物的一個實體,它是構成系統的一個基本單位。一
個對象由一組屬性和對這組屬性進行操作的一組服務組成。從更抽象的角度來說,對象是問題域或
實現域中某些事物的一個抽象,它反映該事物在系統中需要保存的信息和發揮的作用;它是一組屬
性和有權對這些屬性進行操作的一組服務的封裝體。客觀世界是由對象和對象之間的聯系組成的。
類與對象的關系就如模具和鑄件的關系,類的實例化結果就是對象,而對一類對象的抽象就是
類。類描述了一組有相同特性(屬性)和相同行為(方法)的對象。
上面大概就是它們的定義吧,也許你是剛接觸面向對象的朋友, 不要被概念的東西搞暈了,給
你舉個例子吧,如果你去中關村想買幾臺組裝的 PC 機,到了那里你第一步要干什么,是不是裝機
的工程師和你坐在一起,按你提供的信息和你一起完成一個裝機的配置單呀,這個配置單就可以想
象成是類,它就是一張紙,但是它上面記錄了你要買的 PC 機的信息,如果用這個配置單買 10 臺機
器,那么這 10 臺機子,都是按這個配置單組成的,所以說這 10 臺機子是一個類型的,也可以說是
一類的。那么什么是對象呢,類的實例化結果就是對象,用這個配置單配置出來(實例化出來)的
機子就是對象,是我們可以操作的實體,10 臺機子,10 個對象。每臺機子都是獨立的,只能說明
他們是同一類的,對其中一個機做任何動作都不會影響其它 9 臺機器,但是我對類修改,也就是在
這個配置單上加一個或少一個配件,那么裝出來的 9 個機子都改變了,這是類和對象的關系(類的
實例化結果就是對象)。
3.什么是面向對象編程呢?
就不說他的概念,如果你想建立一個電腦教室,首先要有一個房間, 房間里面要有 N 臺電腦,
有 N 張桌子, N 把椅子, 白板, 投影機等等,這些是什么,剛才咱們說了,這就是對象,能看
到的一個個的實體,可以說這個電腦教室的單位就是這一個個的實體對象, 它們共同組成了這個
電腦教室,那么我們是做程序,這和面向對象有什么關系呢?開發一個系統程序和建一個電腦教室
類似,你把每個獨立的功能模塊抽象成類,形成對象,由多個對象組成這個系統,這些對象之間都
能夠接收信息、處理數據和向其它對象發送信息等等相互作用。就構成了面向對象的程序。
4.如何抽象出一個類?
上面已經介紹過了,面向對象程序的單位就是對象,但對象又是通過類的實例化出來的,所以
我們首先要做的就是如何來聲明類,做出來一個類很容易,只要掌握基本的程序語法定義規則就可
以做的出來,那么難點在那里呢?一個項目要用到多少個類,用多少個對象,在那要定義類,定義
一個什么樣的類,這個類實例化出多少個對象,類里面有多少個屬性,有多少個方法等等,這就需
要讀者通過在實際的開發中就實際問題分析設計和總結了。
類的定義:
class 類名{
}
使用一個關鍵字 class 和后面加上一個你想要的類名以及加上一對大括號, 這樣一個類的結構
就定義出來了,只要在里面寫代碼就可以了,但是里面寫什么?能寫什么?怎樣寫才是一個完整的
類呢?上面講過來,使用類是為了讓它實例出對象來給我們用,這就要知道你想要的是什么樣的對
象了,像上面我們講的一個裝機配置單上寫什么,你裝出來的機子就有什么。比如說,一個人就是
一個對象,你怎么把一個你看好的人推薦給你們領導呢?當然是越詳細越好了:
首先,你會介紹這個人姓名、性別、年齡、身高、體重、電話、家庭住址等等。
然后,你要介紹這個人能做什么,可以開車,會說英語,可以使用電腦等等。
只要你介紹多一點,別人對這個人就多一點了解,這就是我們對一個人的描述, 現在我們總結
一下,所有的對象我們用類去描述都是類似的,從上面人的描述可以看到, 做出一個類來,從定
義的角度分兩部分,第一是從靜態上描述,第二是從動態上描述, 靜態上的描述就是我們所說的
屬性,像上面我們看到的,人的姓名、性別、年齡、身高、體重、電話、家庭住址等等。動態上也
就是人的這個對象的功能,比如這個人可以開車,會說英語,可以使用電腦等等,抽象成程序時,
我們把動態的寫成函數或者說是方法,函數和方法是一樣的。所以,所有類都是從屬性和方法這兩
方面去寫, 屬性又叫做這個類的成員屬性,方法叫做這個類的成員方法。
class 人{
成員屬性:姓名、性別、年齡、身高、體重、電話、家庭住址
成員方法:可以開車, 會說英語, 可以使用電腦
}
屬性:
通過在類定義中使用關鍵字" var "來聲明變量,即創建了類的屬性,雖然在聲明成員屬性
的時候可以給定初始值, 但是在聲明類的時候給成員屬性初始值是沒有必要的,比如說要
是把人的姓名賦上“張三”,那么用這個類實例出幾十個人,這幾十個人都叫張三了,所以
沒有必要, 我們在實例出對象后給成員屬性初始值就可以了。
如: var $somevar;
方法(成員函數):
通過在類定義中聲明函數,即創建了類的方法。
如: function somefun(參數列表)
{ ... ... }
<?php
class Person
{
//下面是人的成員屬性
var $name; //人的名字
var $sex; //人的性別
var $age; //人的年齡
//下面是人的成員方法
function say() //這個人可以說話的方法
{
echo "這個人在說話";
}f
unction run() //這個人可以走路的方法
{
echo "這個人在走路";
}
}
?>
上面就是一個類的聲明,從屬性和方法上聲明出來的一個類,但是成員屬性最好在聲明的時候
不要給初始的值,因為我們做的人這個類是一個描述信息,將來用它實例化對象,比如實例化出來
10 個人對象,那么這 10 個人, 每一個人的名字、性別、年齡都是不一樣的,所以最好不要在這個
地方給成員屬性賦初值,而是對每個對象分別賦值的。
用同樣的辦法可以做出你想要的類了,只要你能用屬性和方法能描述出來的實體都可以定義成
類,去實例化對象。
為了加強你對類的理解,我們再做一個類,做一個形狀的類,形狀的范圍廣了點, 我們就做個
矩形吧,先分析一下,想一想從兩方面分析,矩形的屬性都有什么?矩形的功能都有什么?
class 矩形
{
//矩形的屬性
矩形的長;
矩形的寬;
//矩形的方法
矩形的周長;
矩形的面積;
}
<?php
class Rect
{
var $kuan;
var $gao;
function zhouChang()
{
計算矩形的周長;
}f
unction mianJi()
{
計算矩形的面積;
}
}
?>
如果用這個類來創建出多個矩形對象,每個矩形對象都有自己的長和寬, 都可以求出自己的周
長和面積了。
類的聲明我們就到這里吧!!
5.如何實例化對象
我們上面說過面向對象程序的單位就是對象,但對象又是通過類的實例化出來的,既然我們類
會聲明了,下一步就是實例化對象了。
當定義好類后,我們使用 new 關鍵字來生成一個對象。
$對象名稱 = new 類名稱();
<?php
class Person
{
//下面是人的成員屬性
var $name; //人的名字
var $sex; //人的性別
var $age; //人的年齡
//下面是人的成員方法
function say() //這個人可以說話的方法
{
echo "這個人在說話";
}f
unction run() //這個人可以走路的方法
{
echo "這個人在走路";
}
}
$p1=new Person();
$p2=new Person();
$p3=new Person();
?>
$p1=new Person();
這條代碼就是通過類產生實例對象的過程,$p1 就是我們實例出來的對象名稱,同理,$p2, $p3
也是我們實例出來的對象名稱,一個類可以實例出多個對象,每個對象都是獨立的,上面的代碼相
當于實例出來 3 個人來,每個人之間是沒有聯系的,只能說明他們都是人類,每個人都有自己的姓
名,性別和年齡的屬性,每個人都有說話和走路的方法,只要是類里面體現出來的成員屬性和成員
方法,實例化出來的對象里面就包含了這些屬性和方法。
對像在 PHP 里面和整型、浮點型一樣,也是一種數據類,都是存儲不同類型數據用的,在運行
的時候都要加載到內存中去用, 那么對象在內存里面是怎么體現的呢?內存從邏輯上說大體上是
分為 4 段,棧空間段、堆空間段、代碼段、初始化靜態段,程序里面不同的聲明放在不同的內存段
里面,棧空間段是存儲占用相同空間長度并且占用空間小的數據類型的地方,比如說整型 1,10,
100,1000,10000,100000 等等,在內存里面占用空間是等長的,都是 64 位 4 個字節。 那么數據
長度不定長,而且占有空間很大的數據類型的數據放在那內存的那個段里面呢?這樣的數據是放在
堆內存里面的。棧內存是可以直接存取的,而堆內存是不可以直接存取的內存。對于我們的對象來
數就是一種大的數據類型而且是占用空間不定長的類型,所以說對象是放在堆里面的,但對象名稱
是放在棧里面的,這樣通過對象名稱就可以使用對象了。
$p1=new Person();
對于這個條代碼, $p1 是對象名稱在棧內存里面,new Person()是真正的對象是在堆內存里面
的,具體的請看下圖:
從上圖可以看出$p1=new Person();等號右邊是真正的對象實例,在堆內存里面的實體,上圖一
共有 3 次 new Person(),所以會在堆里面開辟 3 個空間,產生 3 個實例對象,每個對象之間都是相
互獨立的,使用自己的空間,在 PHP 里面,只要有一個 new 這個關鍵字出現就會實例化出來一個對
象,在堆里面開辟一塊自己的空間。
每個在堆里面的實例對象是存儲屬性的,比如說,現在堆里面的實例對象里面都存有姓名、性
別和年齡。每個屬性又都有一個地址。
$p1=new Person();等號的右邊$p1 是一個引用變量,通過賦值運算符“=”把對象的首地址賦
給“$p1”這個引用變量,所以$p1 是存儲對象首地址的變量,$p1 放在棧內存里邊,$p1 相當于一
個指針指向堆里面的對象,所以我們可以通過$p1 這個引用變量來操作對象,通常我們也稱對象引用
為對象。
6.如何去使用對象中的成員
上面看到 PHP 對象中的成員有兩種一種是成員屬性,一種是成員方法。對象我們以經可以聲明
了,$p1=new Person();怎么去使用對象的成員呢?要想訪問對象中的成員就要使用一個特殊的操
作符“->”來完成對象成員的訪問:
對象->屬性 $p1->name; $p2->age; $p3->sex;
對象->方法 $p1->say(); $p2->run();
如下面實例:
<?php
class Person
{
//下面是人的成員屬性
var $name; //人的名字
var $sex; //人的性別
var $age; //人的年齡
//下面是人的成員方法
function say() //這個人可以說話的方法
{
echo "這個人在說話";
}f
unction run() //這個人可以走路的方法
{
echo "這個人在走路";
}
}
$p1=new Person(); //創建實例對象$p1
$p2=new Person(); //創建實例對象$p2
$p3=new Person(); //創建實例對象$p3
//下面三行是給$p1對象屬性賦值
$p1->name=”張三”;
$p1->sex=”男”;
$p1->age=20;
//下面三行是訪問$p1對象的屬性
echo “p1對象的名字是:”.$p1->name.”<br>”;
echo “p1對象的性別是:”.$p1->sex.”<br>”;
echo “p1對象的年齡是:”.$p1->age.”<br>”;
//下面兩行訪問$p1對象中的方法
$p1->say();
$p1->run();
//下面三行是給$p2對象屬性賦值
$p2->name=”李四”;
$p2->sex=”女”;
$p2->age=30;
//下面三行是訪問$p2對象的屬性
echo “p2對象的名字是:”.$p2->name.”<br>”;
echo “p2對象的性別是:”.$p2->sex.”<br>”;
echo “p2對象的年齡是:”.$p2->age.”<br>”;
//下面兩行訪問$p2對象中的方法
$p2->say();
$p2->run();
//下面三行是給$p3對象屬性賦值
$p3->name=”王五”;
$p3->sex=”男”;
$p3->age=40;
//下面三行是訪問$p3對象的屬性
echo “p3對象的名字是:”.$p3->name.”<br>”;
echo “p3對象的性別是:”.$p3->sex.”<br>”;
echo “p3對象的年齡是:”.$p3->age.”<br>”;
//下面兩行訪問$p3對象中的方法
$p3->say();
$p3->run();
?>
從上例中可以看出只是對象里面的成員就要使用對象->屬性、對象->方法形式訪問,再沒有第
二種方法來訪問對象中的成員了。
7.特殊的引用“$this”的使用
現在我們知道了如何訪問對象中的成員,是通過“對象->成員”的方式訪問的,這是在對象的外
部去訪問對象中成員的形式,那么如果我想在對象的內部,讓對象里的方法訪問本對象的屬性,或
是對象中的方法去調用本對象的其它方法這時我們怎么辦?因為對象里面的所有的成員都要用對
象來調用,包括對象的內部成員之間的調用,所以在 PHP 里面給我提供了一個本對象的引用$this,
每個對象里面都有一個對象的引用$this 來代表這個對象,完成對象內部成員的調用, this 的本意就是
“這個”的意思,上面的實例里面,我們實例化三個實例對象$P1、$P2、$P3,這三個對象里面各
自存在一個$this 分別代表對象$p1、$p2、$p3 。
通過上圖我們可以看到,$this 就是對象內部代表這個對象的引用,在對象內部和調用本對象的
成員和對象外部調用對象的成員所使用的方式是一樣的。
$this->屬性 $this->name; $this->age; $this->sex;
$this->方法 $this->say(); $this->run();
修改一下上面的實例,讓每個人都說出自己的名字,性別和年齡:
<?php
class Person
{
//下面是人的成員屬性
var $name; //人的名字
var $sex; //人的性別
var $age; //人的年齡
//下面是人的成員方法
function say() //這個人可以說話的方法
{
echo "我的名字叫:".$this->name." 性別:".$this->sex." 我的年齡是:
".$this->age."<br>";
}f
unction run() //這個人可以走路的方法
{
echo "這個人在走路";
}
}
$p1=new Person(); //創建實例對象$p1
$p2=new Person(); //創建實例對象$p2
$p3=new Person(); //創建實例對象$p3
//下面三行是給$p1對象屬性賦值
$p1->name="張三";
$p1->sex="男";
$p1->age=20;
//下面訪問$p1對象中的說話方法
$p1->say();
//下面三行是給$p2對象屬性賦值
$p2->name="李四";
$p2->sex="女";
$p2->age=30;
//下面訪問$p2對象中的說話方法
$p2->say();
//下面三行是給$p3對象屬性賦值
$p3->name="王五";
$p3->sex="男";
$p3->age=40;
//下面訪問$p3對象中的說話方法
$p3->say();
?>
輸出結果為:
我的名字叫:張三 性別:男 我的年齡是:20
我的名字叫:李四 性別:女 我的年齡是:30
我的名字叫:王五 性別:男 我的年齡是:40
分析一下這個方法:
function say() //這個人可以說話的方法
{
echo "我的名字叫:".$this->name." 性別:".$this->sex." 我的年齡是:
".$this->age."<br>";
}
在$p1、$p2 和$p3 這三個對象中都有 say()這個方法,$this 分別代表這三個對象, 調用相應
的屬性,打印出屬性的值,這就是在對象內部訪問對象屬性的方式, 如果相在 say()這個方法里調
用 run()這個方法也是可以的,在 say()這個方法中使用$this->run()的方式來完成調用。
8.構造方法與析構方法
大多數類都有一種稱為構造函數的特殊方法。當創建一個對象時,它將自動調用構造函數,也
就是使用 new 這個關鍵字來實例化對象的時候自動調用構造方法。
構造函數的聲明與其它操作的聲明一樣,只是其名稱必須是__construct( )。這是 PHP5 中的變化,
以前的版本中,構造函數的名稱必須與類名相同,這種在 PHP5 中仍然可以用,但現在以經很少有
人用了,這樣做的好處是可以使構造函數獨立于類名,當類名發生改變時不需要改相應的構造函數
名稱了。為了向下兼容,如果一個類中沒有名為__construct( )的方法,PHP 將搜索一個 php4 中的寫
法,與類名相同名的構造方法。
格式:function __construct ( [參數] ) { ... ... }
在一個類中只能聲明一個構造方法,而是只有在每次創建對象的時候都會去調用一次構造方法,
不能主動的調用這個方法,所以通常用它執行一些有用的初始化任務。比如對成屬性在創建對象的
時候賦初值。
<?
//創建一個人類
class Person
{
//下面是人的成員屬性
var $name; //人的名字
var $sex; //人的性別
var $age; //人的年齡
//定義一個構造方法參數為姓名$name、性別$sex和年齡$age
function __construct($name, $sex, $age)
{
//通過構造方法傳進來的$name給成員屬性$this->name賦初使值
$this->name=$name;
//通過構造方法傳進來的$sex給成員屬性$this->sex賦初使值
$this->sex=$sex;
//通過構造方法傳進來的$age給成員屬性$this->age賦初使值
$this->age=$age;
}/
/這個人的說話方法
function say()
{
echo "我的名字叫:".$this->name." 性別:".$this->sex." 我的年齡是:
".$this->age."<br>";
}
}
//通過構造方法創建3個對象$p1、p2、$p3,分別傳入三個不同的實參為姓名、性別和年齡
$p1=new Person(“張三”,”男”, 20);
$p2=new Person(“李四”,”女”, 30);
$p3=new Person(“王五”,”男”, 40);
//下面訪問$p1對象中的說話方法
$p1->say();
//下面訪問$p2對象中的說話方法
$p2->say();
//下面訪問$p3對象中的說話方法
$p3->say();
?>
輸出結果為:
我的名字叫:張三 性別:男 我的年齡是:20
我的名字叫:李四 性別:女 我的年齡是:30
我的名字叫:王五 性別:男 我的年齡是:40
如圖:
析構函數:
與構造函數相對的就是析構函數。析構函數是 PHP5 新添加的內容,在 PHP4 中沒有析構函數。
析構函數允許在銷毀一個類之前執行的一些操作或完成一些功能,比如說關閉文件,釋放結果集等,
析構函數會在到某個對象的所有引用都被刪除或者當對象被顯式銷毀時執行,也就是對象在內存中
被銷毀前調用析構函數。與構造函數的名稱類似,一個類的析構函數名稱必須是__destruct( )。析構
函數不能帶有任何參數。
格式:function __destruct ( ) { ... ... }
<?
//創建一個人類
class Person
{
//下面是人的成員屬性
var $name; //人的名字
var $sex; //人的性別
var $age; //人的年齡
//定義一個構造方法參數為姓名$name、性別$sex和年齡$age
function __construct($name, $sex, $age)
{
//通過構造方法傳進來的$name給成員屬性$this->name賦初使值
$this->name=$name;
//通過構造方法傳進來的$sex給成員屬性$this->sex賦初使值
$this->sex=$sex;
//通過構造方法傳進來的$age給成員屬性$this->age賦初使值
$this->age=$age;
}
//這個人的說話方法
function say()
{
echo "我的名字叫:".$this->name." 性別:".$this->sex." 我的年齡是:
".$this->age."<br>";
}
//這是一個析構函數,在對象銷毀前調用
function __destruct()
{
echo “再見”.$this->name.”<br>”;
}
//通過構造方法創建3個對象$p1、p2、$p3,分別傳入三個不同的實參為姓名、性別和年齡
$p1=new Person(“張三”,”男”, 20);
$p2=new Person(“李四”,”女”, 30);
$p3=new Person(“王五”,”男”, 40);
//下面訪問$p1對象中的說話方法
$p1->say();
//下面訪問$p2對象中的說話方法
$p2->say();
//下面訪問$p3對象中的說話方法
$p3->say();
?>
輸出結果為:
我的名字叫:張三 性別:男 我的年齡是:20
我的名字叫:李四 性別:女 我的年齡是:30
我的名字叫:王五 性別:男 我的年齡是:40
再見張三
再見李四
再見王五
9.封裝性
封裝性是面向對象編程中的三大特性之一,封裝性就是把對象的屬性和服務結合成一個獨立的
相同單位,并盡可能隱蔽對象的內部細節,包含兩個含義:1.把對象的全部屬性和全部服務結合在一
起,形成一個不可分割的獨立單位(即對象)。2.信息隱蔽,即盡可能隱蔽對象的內部細節,對外形
成一個邊界〔或者說形成一道屏障〕,只保留有限的對外接口使之與外部發生聯系。
封裝的原則在軟件上的反映是:要求使對象以外的部分不能隨意存取對象的內部數據(屬性),
從而有效的避免了外部錯誤對它的"交叉感染",使軟件錯誤能夠局部化,大大減少查錯和排錯的難
度。
用個實例來說明吧,假如某個人的對象中有年齡和工資等屬性,像這樣個人隱私的屬性是不想
讓其它人隨意就能獲得到的,如果你不使用封裝,那么別人想知道就能得到,但是如果你封裝上之
后別人就沒有辦法獲得封裝的屬性,除非你自己把它說出去,否則別人沒有辦法得到。
再比如說,個人電腦都有一個密碼,不想讓其它人隨意的登陸,在你的電腦里面拷貝和粘貼。
還有就是像人這個對象,身高和年齡的屬性,只能是自己來增長,不可以讓別人隨意的賦值等等。
使用 private 這個關鍵字來對屬性和方法進行封裝:
原來的成員:
var $name; //聲明人的姓名
var $sex; //聲明人的性別
var $age; //聲明人的年齡
function run(){… … .}
改成封裝的形式:
private $name; //把人的姓名使用 private 關鍵字進行封裝
private $sex; //把人的性別使用 private 關鍵字進行封裝
private $age; //把人的年齡使用 private 關鍵字進行封裝
private function run(){… … } //把人的走路方法使用 private 關鍵字進行封裝
注意:只要是成員屬性前面有其它的關鍵字就要去掉原有的關鍵字“var”。
通過 private 就可以把人的成員(成員屬性和成員方法)封裝上了。封裝上的成員就不能被類外
面直接訪問了,只有對象內部自己可以訪問;下面的代碼會產生錯誤:
class Person
{
//下面是人的成員屬性
private $name; //人的名字,被private封裝上了
private $sex; //人的性別, 被private封裝上了
private $age; //人的年齡, 被private封裝上了
//這個人可以說話的方法
function say()
{
echo "我的名字叫:".$this->name." 性別:".$this->sex." 我的年齡是:
".$this->age."<br>";
}/
/這個人可以走路的方法, 被private封裝上了
private function run()
{
echo "這個人在走路";
}
}
//實例化一個人的實例對象
$p1=new Person();
//試圖去給私有的屬性賦值, 結果會發生錯誤
$p1->name="張三";
$p1->sex="男";
$p1->age=20;
//試圖去打印私有的屬性, 結果會發生錯誤
echo $p1->name.”<br>”;
echo $p1->sex.”<br>”;
echo $p1->age.”<br>”
//試圖去打印私有的成員方法, 結果會發生錯誤
$p1->run();
輸出結果為:
Fatal error: Cannot access private property Person::$name
Fatal error: Cannot access private property Person::$sex
Fatal error: Cannot access private property Person::$age
Fatal error: Cannot access private property Person::$name
Fatal error: Call to private method Person::run() from context ''
從上面的實例可以看到,私有的成員是不能被外部訪問的,因為私有成員只能在本對象內部自
己訪問,比如,$p1 這個對象自己想把他的私有屬性說出去,在 say()這個方法里面訪問了私有屬性,
這樣是可以。(沒有加任何訪問控制,默認的是 public 的,任何地方都可以訪問)
//這個人可以說話的方法, 說出自己的私有屬性,在這里也可以訪問私有方法
function say()
{
echo "我的名字叫:".$this->name." 性別:".$this->sex." 我的年齡是:
".$this->age."<br>";
//在這里也可以訪問私有方法
//$this->run();
}
因為成員方法 say()是公有的, 所以我們在類的外部調用 say()方法是可以的,改變上面的代碼;
class Person
{
//下面是人的成員屬性
private $name; //人的名字,被private封裝上了
private $sex; //人的性別, 被private封裝上了
private $age; //人的年齡, 被private封裝上了
//定義一個構造方法參數為私有的屬性姓名$name、性別$sex和年齡$age進行賦值
function __construct($name, $sex, $age)
{
//通過構造方法傳進來的$name給私有成員屬性$this->name賦初使值
$this->name=$name;
//通過構造方法傳進來的$sex給私有成員屬性$this->sex賦初使值
$this->sex=$sex;
//通過構造方法傳進來的$age給私有成員屬性$this->age賦初使值
$this->age=$age;
}/
/這個人可以說話的方法, 說出自己的私有屬性,在這里也可以訪問私有方法
function say()
{
echo "我的名字叫:".$this->name." 性別:".$this->sex." 我的年齡是:
".$this->age."<br>";
}
}
//通過構造方法創建3個對象$p1、p2、$p3,分別傳入三個不同的實參為姓名、性別和年齡
$p1=new Person(“張三”,”男”, 20);
$p2=new Person(“李四”,”女”, 30);
$p3=new Person(“王五”,”男”, 40);
//下面訪問$p1對象中的說話方法
$p1->say();
//下面訪問$p2對象中的說話方法
$p2->say();
//下面訪問$p3對象中的說話方法
$p3->say();
輸出結果為:
我的名字叫:張三 性別:男 我的年齡是:20
我的名字叫:李四 性別:女 我的年齡是:30
我的名字叫:王五 性別:男 我的年齡是:40
因為構造方法是默認的公有方法(構造方法不要設置成私有的),所以在類的外面可以訪問到,
這樣就可以使用構造方法創建對象, 另外構造方法也是類里面的函數,所以可以用構造方法給私
有的屬性賦初值。Say()的方法是默認公有的, 所以在外面也可以訪問的到, 說出他自己的私有屬
性。
從上面的例子中我們可以看到,私有的成員只能在類的內部使用,不能被類外部直接來存取,
但是在類的內部是有權限訪問的,所以有時候我們需要在類的外面給私有屬性賦值和讀取出來,也
就是給類的外部提供一些可以存取的接口,上例中構造方法就是一種賦值的形式,但是構造方法只
是在創建對象的時候賦值,如果我們已經有一個存在的對象了,想對這個存在的對象賦值,這個時
候,如果你還使用構造方法傳值的形式傳值,那么就創建了一個新的對象,并不是這個已存在的對
象了。所以我們要對私有的屬性做一些可以被外部存取的接口,目的就是可以在對象存在的情況下,
改變和存取屬性的值,但要注意,只有需要讓外部改變的屬性才這樣做,不想讓外面訪問的屬性是
不做這樣的接口的,這樣就能達到封裝的目的,所有的功能都是對象自己來完成,給外面提供盡量
少的操作。
如果給類外部提供接口,可以為私有屬性在類外部提供設置方法和獲取方法,來操作私有屬性.
例如:
prvate $age; //私有的屬性年齡
function setAge($age) //為外部提供一個公有設置年齡的方法
{
if($age<0 || $age>130) //在給屬性賦值的時候,為了避免非法值設置給屬性
return;
$this->age=$age;
}f
unction getAge() //為外部提供一個公有獲取年齡的方法
{
return($this->age);
}
上面的方法是為一個成員屬性設置和獲取值, 當然你也可以為每個屬性用同樣的方法對其進行
賦值和取值的操作,完成在類外部的存取工作。
10.__set() __get() __isset() __unset()四個方法的應用
一般來說,總是把類的屬性定義為 private,這更符合現實的邏輯。但是,對屬性的讀取和賦值
操作是非常頻繁的,因此在 PHP5 中,預定義了兩個函數“__get()”和“__set()”來獲取和賦值其
屬性,以及檢查屬性的“__isset()”和刪除屬性的方法“__unset()”。
上一節中,我們為每個屬性做了設置和獲取的方法,在 PHP5 中給我們提供了專門為屬性設置
值和獲取值的方法,“__set()”和“__get()”這兩個方法,這兩個方法不是默認存在的,而是我們手
工添加到類里面去的,像構造方法(__construct())一樣, 類里面添加了才會存在,可以按下面的方式
來添加這兩個方法,當然也可以按個人的風格來添加:
//__get()方法用來獲取私有屬性
private function __get($property_name)
{
if(isset($this->$property_name))
{
return($this->$property_name);
}else
{
return(NULL);
}
}/
/__set()方法用來設置私有屬性
private function __set($property_name, $value)
{
$this->$property_name = $value;
}
__get()方法:這個方法用來獲取私有成員屬性值的,有一個參數,參數傳入你要獲取的成員屬性
的名稱,返回獲取的屬性值,這個方法不用我們手工的去調用,因為我們也可以把這個方法做成私
有的方法,是在直接獲取私有屬性的時候對象自動調用的。因為私有屬性已經被封裝上了,是不能
直接獲取值的(比如:“echo $p1->name”這樣直接獲取是錯誤的),但是如果你在類里面加上了這
個方法,在使用“echo $p1->name”這樣的語句直接獲取值的時候就會自動調用__get($property_name)
方法,將屬性 name 傳給參數$property_name,通過這個方法的內部執行,返回我們傳入的私有屬性
的值。如果成員屬性不封裝成私有的,對象本身就不會去自動調用這個方法。
__set()方法:這個方法用來為私有成員屬性設置值的,有兩個參數,第一個參數為你要為設置
值的屬性名,第二個參數是要給屬性設置的值,沒有返回值。這個方法同樣不用我們手工去調用,
它也可以做成私有的,是在直接設置私有屬性值的時候自動調用的,同樣屬性私有的已經被封裝上
了, 如果沒有__set()這個方法,是不允許的, 比如:$this->name=‘zhangsan’, 這樣會出錯,但
是如果你在類里面加上了__set($property_name, $value)這個方法,在直接給私有屬性賦值的時候,
就會自動調用它,把屬性比如 name 傳給$property_name, 把要賦的值“zhangsan”傳給$value,通過
這個方法的執行,達到賦值的目的。如果成員屬性不封裝成私有的,對象本身就不會去自動調用這
個方法。為了不傳入非法的值, 還可以在這個方法給做一下判斷。代碼如下:
<?php
class Person
{
//下面是人的成員屬性, 都是封裝的私有成員
private $name; //人的名字
private $sex; //人的性別
private $age; //人的年齡
//__get()方法用來獲取私有屬性
private function __get($property_name)
{
echo "在直接獲取私有屬性值的時候,自動調用了這個__get()方法<br>";
if(isset($this->$property_name))
{
return($this->$property_name);
}e
lse
{
return(NULL);
}
}/
/__set()方法用來設置私有屬性
private function __set($property_name, $value)
{
echo "在直接設置私有屬性值的時候,自動調用了這個__set()方法為私有屬性賦值<br>";
$this->$property_name = $value;
}
}
$p1=new Person();
//直接為私有屬性賦值的操作, 會自動調用__set()方法進行賦值
$p1->name="張三";
$p1->sex="男";
$p1->age=20;
//直接獲取私有屬性的值, 會自動調用__get()方法,返回成員屬性的值
echo "姓名:".$p1->name."<br>";
echo "性別:".$p1->sex."<br>";
echo "年齡:".$p1->age."<br>";
?>
程序執行結果:
在直接設置私有屬性值的時候,自動調用了這個__set()方法為私有屬性賦值
在直接設置私有屬性值的時候,自動調用了這個__set()方法為私有屬性賦值
在直接設置私有屬性值的時候,自動調用了這個__set()方法為私有屬性賦值
在直接獲取私有屬性值的時候,自動調用了這個__get()方法
姓名:張三
在直接獲取私有屬性值的時候,自動調用了這個__get()方法
性別:男
在直接獲取私有屬性值的時候,自動調用了這個__get()方法
年齡:20
以上代碼如果不加上__get()和__set()方法,程序就會出錯,因為不能在類的外部操作私有成員,
而上面的代碼是通過自動調用__get()和__set()方法來幫助我們直接存取封裝的私有成員的。
__isset() 方法:在看這個方法之前我們看一下“isset()”函數的應用,isset()是測定變量是否設
定用的函數,傳入一個變量作為參數,如果傳入的變量存在則傳回 true,否則傳回 false。那么如果
在一個對象外面使用“isset()”這個函數去測定對象里面的成員是否被設定可不可以用它呢?分兩種
情況,如果對象里面成員是公有的,我們就可以使用這個函數來測定成員屬性,如果是私有的成員
屬性,這個函數就不起作用了,原因就是因為私有的被封裝了,在外部不可見。那么我們就不可以
在對象的外部使用“isset()”函數來測定私有成員屬性是否被設定了呢?可以,你只要在類里面加上
一個“__isset()”方法就可以了,當在類外部使用”isset()”函數來測定對象里面的私有成員是否被設
定時, 就會自動調用類里面的“__isset()”方法了幫我們完成這樣的操作,“__isset()”方法也可以
做成私有的。你可以在類里面加上下面這樣的代碼就可以了:
private function __isset($nm)
{
echo "當在類外部使用isset()函數測定私有成員$nm時,自動調用<br>";
return isset($this->$nm);
}
__unset()方法:看這個方法之前呢,我們也先來看一下“unset()”這個函數,“unset()”這個函
數的作用是刪除指定的變量且傳回 true,參數為要刪除的變量。那么如果在一個對象外部去刪除對
象內部的成員屬性用“unset()”函數可不可以呢,也是分兩種情況,如果一個對象里面的成員屬性
是公有的,就可以使用這個函數在對象外面刪除對象的公有屬性,如果對象的成員屬性是私有的,
我使用這個函數就沒有權限去刪除,但同樣如果你在一個對象里面加上“__unset()”這個方法,就
可以在對象的外部去刪除對象的私有成員屬性了。在對象里面加上了“__unset()”這個方法之后,
在對象外部使用“unset()”函數刪除對象內部的私有成員屬性時,自動調用“__unset()”函數來幫
我們刪除對象內部的私有成員屬性,這個方法也可以在類的內部定義成私有的。在對象里面加上下
面的代碼就可以了:
private function __unset($nm)
{
echo "當在類外部使用unset()函數來刪除私有成員時自動調用的<br>";
unset($this->$nm);
}
我們來看一個完整的實例:
<?php
class Person
{
//下面是人的成員屬性
private $name; //人的名字
private $sex; //人的性別
private $age; //人的年齡
//__get()方法用來獲取私有屬性
private function __get($property_name)
{
if(isset($this->$property_name))
{
return($this->$property_name);
}else {
return(NULL);
}
}/
/__set()方法用來設置私有屬性
private function __set($property_name, $value)
{
$this->$property_name = $value;
}/
/__isset()方法
private function __isset($nm)
{
echo "isset()函數測定私有成員時,自動調用<br>";
return isset($this->$nm);
}/
/__unset()方法
private function __unset($nm)
{
echo "當在類外部使用unset()函數來刪除私有成員時自動調用的<br>";
unset($this->$nm);
}
}
$p1=new Person();
$p1->name="this is a person name";
//在使用isset()函數測定私有成員時,自動調用__isset()方法幫我們完成,返回結果為true
echo var_dump(isset($p1->name))."<br>";
echo $p1->name."<br>";
//在使用unset()函數刪除私有成員時,自動調用__unset()方法幫我們完成,刪除name私有屬性
unset($p1->name);
//已經被刪除了, 所這行不會有輸出
echo $p1->name;
?>
輸出結果為:
isset()函數測定私有成員時,自動調用
bool(true)
this is a person name
當在類外部使用 unset()函數來刪除私有成員時自動調用的
__set()、__get()、__isset()、__unset() 這四個方法都是我們添加到對象里面的,在需要時自動調
用的,來完成在對象外部對對象內部私有屬性的操作。
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()函數來反串行化對象,這個函數的參數即為 serialize()函數的返回值,
輸出當然是重新組織好的對象.
代碼片斷
<? 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); $p1_string=serialize($p1); //把一個對象串行化,返一個字符串 echo $p1_string."<br>"; //串行化的字符串我們通常不去解析 $p2=unserialize($p1_string); //把一個串行化的字符串反串行化形成對象$p2 $p2->say(); ?>
上例輸出結果:
執行結果
O:6:"Person":3:{s:4:"name";s:4:"張三";s:3:"sex";s:2:"男";s:3:"age";i:20;}
我的名字叫:張三 性別:男 我的年齡是:20
在 php5 中有兩個魔術方法__sleep()方法和__wakeup()方法,在對象串行化的時候,會調用一
個__sleep()方法來完成一些睡前的事情;而在重新醒來,即由二進制串重新組成一個對象的時候,
則會自動調用 PHP 的另一個函數__wakeup(),做一些對象醒來就要做的動作。
__sleep()函數不接受任何參數, 但返回一個數組,其中包含需要串行化的屬性。末被包含的
屬性將在串行化時被忽略,如果沒有__sleep()方法,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>"; } //指定串行化時把返回的數組中$name 和$age 值串行化,忽略沒在數組中的屬性$sex function __sleep() { $arr=array("name", "age"); return($arr); } //重新生成對象時,并重新賦值$age為40 function __wakeup() { $this->age = 40; } } $p1=new Person("張三", "男", 20); //把一個對象串行化,返一個字符串,調用了__sleep()方法,忽略沒在數組中的屬性$sex $p1_string=serialize($p1); echo $p1_string."<br>"; //串行化的字符串我們通常不去解析 $p2=unserialize($p1_string); //反串行化形成對象$p2重新賦值$age為40 $p2->say(); ?>
上例輸出值為:
執行結果
O:6:"Person":2:{s:4:"name";s:4:"張三";s:3:"age";i:20;}
我的名字叫:張三 性別: 我的年齡是:40
23.自動加載類
很多開發者寫面向對象的應用程序時,對每個類的定義建立一個 PHP 源文件。一個很大的煩
惱是不得不在每個腳本(每個類一個文件)開頭寫一個長長的包含文件的列表。
在軟件開發的系統中,不可能把所有的類都寫在一個 PHP 文件中,當在一個 PHP 文件中需要調
用另一個文件中聲明的類時,就需要通過 include 把這個文件引入。不過有的時候,在文件眾多的
項目中,要一一將所需類的文件都 include 進來,是一個很讓人頭疼的事,所以我們能不能在用到
什么類的時候,再把這個類所在的 PHP 文件導入呢?這就是我們這里我們要講的自動加載類。
在 PHP 5 中,可以定義一個__autoload()函數,它會在試圖使用尚未被定義的類時自動調用,
通過調用此函數,腳本引擎在 PHP 出錯失敗前有了最后一個機會加載所需的類,__autoload()函
數接收的一個參數,就是你想加載的類的類名,所以你做項目時,在組織定義類的文件名時,需要
按 照 一 定 的 規 則 , 最 好 以 類 名 為 中 心 , 也 可 以 加 上 統 一 的 前 綴 或 后 綴 形 成 文 件 名 , 比 如
xxx_classname.php、classname_xxx.php 以及就是 classname.php 等等。
本例嘗試分別從 MyClass1.php 和 MyClass2.php 文件中加載 MyClass1 和 MyClass2 類
代碼片斷
<?php function __autoload($classname) { require_once $classname . '.php'; }/ /MyClass1 類不存在自動調用__autoload()函數,傳入參數”MyClass1” $obj = new MyClass1(); //MyClass2 類不存在自動調用__autoload()函數,傳入參數”MyClass2” $obj2 = new MyClass2(); ?>