1.理解面向對象的概念
面向對象軟件的一個重要優點是支持和鼓勵封裝的能力。封裝也叫數據隱藏。
在面向對象的軟件中,對象是一個被保存數據和操作這些數據的操作方法的唯一、可標識的集合。
對象可以按類進行分類。
面向對象的編程語言必須支持多態性,多態性的意思是指不同的類對同一操作可以有不同的行為。語言支持多態性,可以知道將哪個操作應用於一個特定的對象。
多態性與其說是對象的特性,不如說是行為的特性。在PHP中,只有類的成員函數可以是多態的。
例如,盡管自行車的“移動”和汽車的“移動”在概念上是相似的,但是移動一輛自行車和移動一輛汽車所包含的行為是完全不同的。一旦行為作用的對象確定下來,動詞“移動”就可以和一系列特定的行為聯系起來。
繼承允許我們使用子類在類之間創建層次關系。子類將從它的超類繼承屬性和操作。
2.在PHP中創建類、屬性和操作
當創建一個PHP類的時候,必須使用關鍵詞“class”。
2.1 類的結構
如下所示代碼創建了一個名為“classname”的類,它具有兩個屬性$attribute1和$attribute2:
class classname{ public $attribute1; public $attribute2; }
通過在類定義中聲明函數,可以創建類的操作。
class classname{ function operation1() { } function operation2($param1,$param2) { } }
2.2 構造函數
大多數類都有一種稱為構造函數的特殊操作。當創建一個對象時,它將調用構造函數,通常,這將執行一些有用的初始化任務:例如,設置屬性的初始值或者創建該對象需要的其他對象。
構造函數其名稱必須是__construct()。這是PHP5中的變化。盡管可以手工調用構造函數,但其本意是在創建一個對象時自動調用。聲明一個具有構造函數的類:
class classname{ function __construct($param){ echo “Constructor called with parameter ”.$param.”<br />”; } }
PHP支持函數重載,這就意味着可以提供多個具有相同名稱以及不同數量或類型的參數的函數。
2.3 析構函數
與構造函數相對的是析構函數。析構函數允許在銷毀一個類之前執行一些操作或完成一些功能,這些操作或功能通常在所有對該類的引用都被重置或超出作用域時自動發生。
與構造函數的名稱類似,一個類的析構函數名稱必須是__destruct()。析構函數不能帶有任何參數。
3.類的實例化
在聲明一個類后,需要創建一個對象(一個特定的個體,即類的一個成員)並使用這個對象。這也叫創建一個實例或實例化一個類。可以使用關鍵詞“new”來創建一個對象。需要指定創建的對象是哪一個類的實例,並且通過構造函數提供任何所需的參數。
如下所示的代碼聲明了一個具有構造函數、名為classname的類,然后又創建3個classname類型的對象。
class classname{ function _construct($param){ echo “Constructor called with parameter ”.$param.”<br />”; } } $a = new classname(“First”); $b = new classname(“Second”); $c = new classname();
運行發現以下報錯:
$c的參數補上:
class classname{ function __construct($param){ echo "Constructor called with parameter ".$param."<br />"; } } $a = new classname("First"); $b = new classname("Second"); $c = new classname("");
4.使用類的屬性
在一個類中,可以訪問一個特殊的指針——$this。如果當前類的一個屬性為$attribute,則當在該類中通過一個操作設置或訪問該變量時,可以使用$this->attribute來引用。
如下所示的代碼說明了如何在一個類中設置和訪問屬性:
class classname{ public $attribute; function operation($param){ $this->attribute = $param; echo $this->attribute; } }
是否可以在類的外部訪問一個屬性是由訪問修飾符來確定的。
通常,從類的外部直接訪問類的屬性是糟糕的想法。面向對象方法的一個優點就是鼓勵使用封裝。可以通過使用__get()和__set()函數來實現對屬性的訪問。
如果不直接訪問一個類的屬性而是編寫訪問函數,那么可以通過一段代碼執行所有訪問。當最初編寫訪問函數時,訪問函數可能如下所示:
class classname{ public $attribute; function __get($name){ return $this->$name; } function __set($name,$value){ $this->$name = $value; } }
以上代碼為訪問$attribute屬性提供了最基本的功能。__get()函數返回了$attribute的值,而__set()函數只是設置了$attribute的值。
注意,__get()函數帶有一個參數(屬性的名稱)並且返回該屬性的值。__set()函數需要兩個參數,分別是:要被設置值的屬性名稱和要被設置的值。
我們並不會直接訪問這些函數。這些函數名稱前面的雙下划線表明在PHP中這些函數具有特殊的意義,就像__construct()函數和__destruct()函數一樣。
這些函數的工作原理是怎樣的?如果實例化一個類:
$a = new classname();
可以用__get()函數和__set()函數來檢查和設置任何屬性的值。
如果使用如下命令:
$a -> $attribute = 5;
該語句將間接調用__set()函數,將$name參數的值設置為“attribute”,而$value的值被設置為5。必須編寫__set()函數來完成任何所需的錯誤檢查。
__get()函數的工作原理類似。如果在代碼中引用:
$a -> attribute;
該語句將間接調用__get()函數,$name參數的值為“attribute”。我們可以自己決定編寫__get()函數來返回屬性值。
我們只使用一段代碼來訪問特定的屬性。只有一個訪問入口,就可以實現對要保存的數據進行檢查,這樣就可以確保被保存的數據是有意義的數據。
如果后來發現$attribute屬性值應該是0到100之間,我們就可以添加幾行代碼,在屬性值改變之前進行檢查:
function _set($name, $value){ if(($name=”attribute”)&&($value>=0)&&($value<=100)) $this->attribute = $value; }
通過單一的訪問入口,可以方便地改變潛在的程序實現。
無論需要做什么樣的改變,只要修改訪問器函數即可。只要保證這個訪問器函數仍然接收並返回程序的其他部分期望的數據類型,那么程序的其他部分代碼就不會受影響。
5.使用private和public關鍵字控制訪問
PHP支持如下3種訪問修飾符:
· 默認選項是public。公有的屬性或方法可以在類的內部和外部進行訪問。
· private訪問修飾符意味着被標記的屬性或方法只能在類的內部進行訪問。
· protected可以理解成位於private和public之間的關鍵字,后面會詳細探討。
6.類操作的調用
與調用屬性大體上相同,可以使用同樣的方式調用類的操作。
如果有如下類:
class classname{ function operation1() { } function operation2($param1,param2) { } }
並且創建了一個類型為classname、名稱為$a的對象,如下所示:
$a = new classname();
可以像調用其他函數一樣調用操作:通過使用其名稱以及將所有所需的參數放置在括號中。因為這些操作屬於一個對象而不是常規的函數,所以需要指定它們所屬的對象。對象名稱的使用方法與對象屬性一樣,如下所示:
$a -> operation1(); $a -> operation2(12,”test”);
如果操作具有返回值,可以捕獲到如下所示的返回數據:
$x = $a->operation1(); $y = $a->operation2(12,”test”);
7.在PHP中實現繼承
繼承是單方向的。子類可以從父類或超類繼承特性,父類缺不能從子類繼承特性。
7.1 通過繼承使用private和protected訪問修飾符控制可見性
可以使用private和protected訪問修飾符來控制需要繼承的內容。 如果一個屬性或方法被指定為private,它將不能被繼承。
如果一個屬性或方法被指定為protected,它將在類外部不可見(就像一個private元素),但是可以被繼承。
考慮如下實例:
<?php class A{ private function operation1(){ echo "operation1 called"; } protected function operation2(){ echo "operation2 called"; } public function operation3(){ echo "operation3 called"; } } class B extends A{ function __construct(){ $this->operation1(); $this->operation2(); $this->operation3(); } } $b = new B; ?>
$this->operation1();這句將產生報錯:
說明私有操作不能在子類中調用。
如果注釋掉這一行,其他兩個函數調用將正常工作。
protected函數可以被繼承但是只能在子類內部使用。如果嘗試在該文件結束處添加如下所示的代碼:
$b->operation2();
將產生如下報錯:
7.2 重載
在子類中,再次聲明相同的屬性和操作也是有效的,而且在有些情況下這將會是非常有用的。我們可能需要在子類中給某個屬性賦予一個與其超類屬性不同的默認值,或者給某個操作賦予一個與其超類操作不同的功能。這就叫重載。
例如,如果有類A:
class A{ public $attribute = "default value"; function operation(){ echo "Something<br />"; echo "The value of \$attribute is ".$this->attribute."<br />"; } }
現在,如果需要改變$attribute的默認值,並為operation()操作提供新的功能,可以創建類B,它重載了$attribute和operation()方法,如下所示:
class B extends A{ public $attribute = "different value"; function operation(){ echo "Something else<br />"; echo "The value of \$attribute is ".$this->attribute."<br />"; } }
聲明類B並不會影響類A的初始定義:
$a = new A(); $a -> operation();
這兩行代碼創建了類A的一個對象並且調用了它的operation()函數:
注意$this->后的變量不帶$符。
如果創建了類B的一個對象:
$b = new B(); $b -> operation();
與子類中定義新的屬性和操作並不影響超類一樣,在子類中重載屬性或操作也不會影響超類。
如果不使用替代,一個子類將繼承超類的所有屬性和操作。如果子類提供了替代定義,替代定義將有優先級並且重載初始定義。
parent關鍵字允許調用父類操作的最初版本。例如,要從類B中調用A::operation,可以使用如下所示的語句:
parent::operation();
但是,其輸出結果卻是不同的。雖然調用了父類的操作,但是PHP將使用當前類的屬性值。
源碼:
<?php class A { public $attribute = "default value"; function operation() { echo "Something<br />"; echo "The value of \$attribute is ".$this->attribute."<br />"; } } class B extends A { public $attribute = "different value"; function operation() { echo "Something else<br />"; echo "The value of \$attribute is ".$this->attribute."<br />"; } function Aoperation() { parent::operation(); } } $a = new A(); $a->operation(); $b = new B(); $b->Aoperation(); ?>
繼承可以是多重的。可以聲明一個類C,它繼承了類B,因此繼承了類B和類B父類的所有特性。類C還可以選擇重載和替換父類的那些屬性和操作。
7.3 使用final關鍵字禁止繼承和重載
當在一個函數聲明前面使用final關鍵字時,這個函數將不能被任何子類中被重載。也可以使用final關鍵字來禁止一個類被繼承。
7.4 PHP不支持多重繼承
如題,每個類只能繼承一個父類。一個父類可以有多少個子類則並沒有限制。
7.5 實現接口
如果需要實現多重繼承功能,在PHP中,可以通過接口。接口可以看作是多重繼承問題的解決方法。
接口的思想是指定一個實現了該接口的類必須實現的一系列函數。例如,需要一系列能夠顯示自身的類。除了可以定義具有display()函數的父類,同時使這些子類都繼承該父類並重載該方法外,還可以實現一個接口:
interface Displayable{ function display(); } class webPage implements Displayable{ function display(){ // ... } }
以上代碼示例說明了多重繼承的一種解決辦法,因為webPage類可以繼承一個類,同時又可以實現一個或多個接口。
如果沒有實現接口中指定的方法,將產生一個致命錯誤。
整理自《PHP和MySQL Web》開發