程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> 網頁編程 >> PHP編程 >> 關於PHP編程 >> php代碼之面向對象基礎一

php代碼之面向對象基礎一

編輯:關於PHP編程

php代碼之面向對象基礎一


這篇文章不適合於初學者看,對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
'; 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();
以上代碼只變動了一處地方,就是把父類$attr1的訪問屬性變成private,那重載機制如何執行呢?先看運行結果:

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的靜態延遲綁定。


寫的有點多,主要是因為一牽扯繼承就停不下來....面向對象還有一些只是點,後面有時間再補上吧...謝謝,如若有錯誤的地方,敬請大家指出,隨時更正,謝謝!!



  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved