這篇文章不適合於初學者看,對php有一定了解的可以看一下,補充或者溫故一下php面向對象裡的一些特性。
一.何為面向對象?
介個問題,雖然略知一二,卻感覺依然拿不出手,只能說將萬事萬物皆看為對象,只有在開發中才能體會出何為面向對象,只說也是徒然,但因為php大多用在web開發,所以,即使不使用面向對象也能運行的不錯,之前在做c++開發時,設計給你個功能界面,看到這個界面,第一件事就是像美工切圖一樣的切成一個個的對象,然後,確定各個對象之間的關系,最後進行開發,哪兒都充斥著此思想。
什麼是類?什麼是對象?
類是一組集合的抽象,什麼集合呢?是一組具有相似特性和操作的對象的集合。
對象是某一個類的具體實例。
畢業時候可能我會背給面試官聽,可現在,雖然感覺理解的還是想書本上的語句一樣的俗,但起碼不用再靠腦細胞說出這兩個定義。
二.php中的類結構
php中類也是那些訪問控制符,也是有屬性和方法。
class Person { private $name = "PersonName"; public static $gender = "PersonGender"; public function test(){ echo $this->name, '
'; } };
構造函數的名稱為__construct,在構造函數這裡我想強調的有以下幾點:
1. php不會像其他語言(c++或者java)那樣,當實例化子類的時候會自動調用你的父類構造函數,php裡需要手動調用父類的構造函數,這兒牽扯到繼承,可以先看一下繼承方面的東西。
class Person{ public funciton __construct(){ echo 'Person construct
'; } }; class Teacher extends Person{ public function __construct(){ //parent::__construct(); echo 'Teacher construct
'; } }; $t1 = new Teacher; //生成Teacher對象
運行結果:
Teacher construct
如果想要在生成子類時初始化父類的一些數據,需要手動調用父類的構造函數,打開注釋行即可。
2. 一個類中不能寫兩個參數不同的構造函數。
這兒牽扯到php中的一些規定,其他語言中,以下寫法是正確的:
class Person{ public funciton __construct(){ echo 'Person construct
'; } public function __construct($param){ echo 'Person with param construct
'; } };
而在php是不允許的,究其根源為php是一種弱語言類型,對於類型的限制不是很敏感,進而提供了__call和func_get_args函數機制,因而可以用如下的方式實現:
class Person{ public function __construct(){ $param = func_get_arg(); //獲取參數數據 $param_num = func_num_args(); //獲取參數個數 if($param_num == 0){ }else if($param_num == 1){ if(is_array($param[0])){ //... } }else{ //... } } };
三.析構函數
析構函數是用於在此實例對象結束時,自動調用的函數,可以寫入一下釋放內存的語句來為實例的死亡畫上完美的句號,這兒與構造相同,有繼承關系時必須手動調用父類的析構,一個類中只有一個析構。
四.控制訪問符
public:公共訪問符,在類內,子類內,類外都可以訪問此屬性或方法。
protected:受保護的訪問符,只能在類內和其子類內訪問此屬性或方法,在類外不能訪問。
private:私有訪問符,只能在本類內訪問,屬於本類私有東東,不能繼承,不能重載,任何人訪問不了。
五.魔術方法__get和__set
這兩個方法的功能:對受保護和私有屬性訪問的一個訪問器,可以對從類外接收到的數據進行安全性和合理性的校驗。
__set方法接收兩個參數,第一個是屬性名稱,第二個是要賦的新值。
__get方法接收一個參數,屬性名稱。
1. public屬性能提供在類外修改屬性的服務,因此,對於public屬性,不會走__get和__set流程。
class D{ public $name = 'D name'; protected $gender = 'male'; private $age = 18; public function __set($name, $value){ echo '__set
'; //if(in_array($name, ['name', 'gender', 'age'])) $this->$name = $value; } public function __get($name){ echo '__get
'; //if(!in_array($name, ['name', 'gender', 'age'])) return NULL; return $this->$name; } };
運行結果:
new D name //name為public屬性,不會走get和set __set __get new D gender __set __get new D age
2. 我們也可以加入數據校驗的功能,打開注釋就可以進行校驗。
3.兩個方法必須是public訪問,否則會提示錯誤,我們可以從這兩個函數的功能來出發思考,就不難想象為什麼需要public訪問控制。
六.繼承
終於到了可以讓你的菊花開苞的特性,沒有繼承,所有類都是渣渣,因為有了繼承,所以...問題就特麼的一大波一大波的來啦...讓我們來深入淺出一下此繼承特性。
什麼繼承就不說了吧,文章的開頭就有一個繼承的小示例。
有了繼承會出現什麼問題呢?想一下,如果B繼承了A...真的是難以想象...
1. 構造函數,這個放心,跟繼承沒有太多關系,相當於兩個類裡的構造函數,但是怎麼也有個父子關系啊,不能把事做的太絕,所以,上面講過,如果有需要,可以手動調用父類的構造函數,可以看下上面的示例。
2.單方向繼承,繼承是單方向的,子類可以從父類繼承,但父類卻不能從子類繼承特性,示例:
class A{ public $attr1; public function oper1(){ } }; class B extends A{ public $attr2; public function oper2(){ } }; //子類可以繼承父類 $b = new B; $b->oper1(); $b->attr1 = 'attr1 value'; $b->oper2(); $b->attr2 = 'attr2 value'; //父類不能繼承子類 $a = new A; $a->oper2();//出錯 $a->attr1();//出錯
3. 重載,一提到php的重載就特別別扭,因為他的重載放到其他語言裡叫做重寫overwrite,我還是習慣將這個特性說為重寫,大家隨便。
<1>public重載:
class E{ public $attr1 = 'E attr1 value'; public function oper1(){ echo 'E oper1
'; echo 'attr1 value = ', $this->attr1, '
'; } }; class F extends E{ public $attr1 = 'F attr1 value'; public function oper1(){ //parent::oper1(); echo 'F oper1
'; echo 'attr1 value = ', $this->attr1, '
'; } }; $f = new F; $f->oper1();
運行結果:
F oper1
attr1 value = F attr1 value
F繼承了E並且重寫了E的attr1和oper1,因此,在調用oper1時,$this->attr1顯示F attr1 value,如果打開注釋parent::oper1調用父類的Oper1方法,運行結果如下:
E oper1
attr1 value = F attr1 value //attr1屬性已經被子類重寫的attr1屬性覆蓋
F oper1
attr1 value = F attr1 value
可以看出子類重寫父類的屬性和方法後,會覆蓋父類相應的屬性和方法。
<2>private重載
class E{ private $attr1 = 'E attr1 value'; public function oper1(){ echo 'E oper1以上代碼只變動了一處地方,就是把父類$attr1的訪問屬性變成private,那重載機制如何執行呢?先看運行結果:
'; echo 'attr1 value = ', $this->attr1, '
'; } }; class F extends E{ public $attr1 = 'F attr1 value'; public function oper1(){ parent::oper1(); echo 'F oper1
'; echo 'attr1 value = ', $this->attr1, '
'; } }; $f = new F; $f->oper1();
E oper1
attr1 value = E attr1 value //父類私有的屬性
F oper1
attr1 value = F attr1 value
之前我們說過,private屬性和方法子類是繼承不了的,這種情況,遵循一個原則:
private屬性在那個類裡調用的,就顯示哪個類裡的屬性值。
示例中的parent::oper1方法調用的是E類的oper1方法,在E的oper1方法內又調用了$this->attr1,attr1是private並沒有被子類繼承,因此,調用的就是類E裡的attr1屬性值。
<3>protected重載與public重載一致
<4>類屬性的繼承
class G{ public static $attr1 = 'G attr1 value'; public function oper1(){ echo 'G oper1
'; echo 'attr1 value = ', self::$attr1, '
'; } }; class H extends G{ public static $attr1 = 'H attr1 value'; public function oper1(){ parent::oper1(); echo 'H oper1
'; echo 'attr1 value = ', self::$attr1, '
'; } }; $h = new H; $h->oper1();
運行結果:
G oper1
attr1 value = G attr1 value
H oper1
attr1 value = H attr1 value
其實不管G類的attr1屬性是public還是private,結果都一樣。
個人是這麼理解的,類屬性可以繼承,但談不上重載,那關於子類調用父類的屬性也有一規則:
self或者parent只代表本類,因此,根據這一原則可以斷定,屬性的值一定是本類屬性的值,與子類無關。(特殊情況時php的靜態延遲加載機制)。
七.靜態延遲加載
既然已經提到了靜態延遲加載,就趁熱打鐵講一下,H和G的例子大家已經看了,那我就是想要在子類中調用父類的東東怎麼辦?靜態延遲加載就是解決這個問題,請看兩個示例:
示例1:
還是H和G類的例子
class G{ public static $attr1 = 'G attr1 value'; public function oper1(){ echo 'G oper1運行結果:
'; echo 'attr1 value = ', static::$attr1, '
'; } }; class H extends G{ public static $attr1 = 'H attr1 value'; public function oper1(){ parent::oper1(); echo 'H oper1
'; echo 'attr1 value = ', self::$attr1, '
'; } }; $h = new H; $h->oper1();
G oper1
attr1 value = H attr1 value
H oper1
attr1 value = H attr1 value
上面代碼只是將G類裡的self::$attr1改寫成了static::$attr1,運行結果就不一樣了
示例2:
class I { public static function who(){ echo __CLASS__, '
'; } public static function test(){ static::who(); } }; class J extends I{ public static function who(){ echo __CLASS__,'
'; } };
運行結果:
J
通過這兩個例子,可以好好的領悟一下static的靜態延遲綁定。
寫的有點多,主要是因為一牽扯繼承就停不下來....面向對象還有一些只是點,後面有時間再補上吧...謝謝,如若有錯誤的地方,敬請大家指出,隨時更正,謝謝!!