官术网_书友最值得收藏!

6章 面向對象

視頻講解:1小時21分鐘

面向對象是一種計算機編程架構,比面向過程編程具有更強的靈活性和擴展性。面向對象編程也是一個程序員發展的“分水嶺”,很多的初學者和略有成就的開發者,就是因為無法理解“面向對象”而放棄。這里想提醒一下初學者:要想在編程這條路上走得比別人遠,就一定要掌握面向對象編程技術。

學習摘要:

面向對象的基本概念

PHP與對象

6.1 面向對象的基本概念

視頻講解

在前幾章中,我們已經學習使用了字符串和數組組織數據,也學習了使用函數把一些代碼收集到能夠反復使用的單元中。本章介紹的對象(object)則讓這種收集的思想更向前邁進一步。對象可以把函數和數據收集在一起。下面來了解一下面向對象的基本概念。

6.1.1 類的概念

世間萬物都具有其自身的屬性和方法,通過這些屬性和方法可以將不同物質區分開來。例如,人具有身高、體重和膚色等屬性,還可以進行吃飯、學習、走路等能動活動,這些活動可以說是人具有的功能。可以把人看作程序中的一個類,那么人的身高可以看作類中的屬性,走路可以看作類中的方法。也就是說,類是屬性和方法的集合,這是面向對象編程方式的核心和基礎。通過類可以將零散的用于實現某項功能的代碼進行有效管理。例如,創建一個運動類,包括5個屬性:姓名、身高、體重、年齡和性別,定義4個方法:踢足球、打籃球、舉重和跳高,如圖6.1所示。

圖6.1 運動類

6.1.2 對象的概念

類只是具備某項功能的抽象模型,實際應用中還需要對類進行實例化,這樣就引入了對象的概念。對象是類進行實例化后的產物,是一個實體。仍然以人為例,“黃種人是人”這句話沒有錯誤,但反過來說“人是黃種人”這句話一定是錯誤的。因為除了有黃種人,還有黑人、白人等。那么“黃種人”就是“人”這個類的一個實例對象。可以這樣理解對象和類的關系:對象實際上就是“有血有肉的、能摸得到看得到的”一個類。

這里實例化6.1.1節中創建的運動類,如圖6.2所示。

圖6.2 實例化對象

6.1.3 面向對象編程的三大特點

面向對象編程的三大特點是封裝性、繼承性和多態性。

1.封裝性

封裝性,也可以稱為信息隱藏。就是將一個類的使用和實現分開,只保留有限的接口(方法)與外部聯系。對于用到該類的開發人員,只要知道這個類該如何使用即可,而不用去關心這個類是如何實現的。這樣做可以讓開發人員更多地把精力集中起來專注別的事情,同時也避免了程序之間相互依賴而帶來的不便。這就像普通用戶購買汽車,我們只需要知道如何駕駛汽車,并不需要去了解汽車內部的構造。

2.繼承性

在真實的世界中,人們可以從他們的父母或者其他直系親戚那里繼承一些東西。例如,在圖6.3中,“我”可以繼承“爸爸”和“媽媽”財產。同理,“爸爸”也可以繼承“祖父”和“祖母”的財產。繼承性就是派生類(子類)自動繼承一個或多個基類(父類)中的屬性與方法,并可以重寫或添加新的屬性或方法。繼承這個特性簡化了對象和類的創建,增加了代碼的可重用性。

圖6.3 家族圖譜

3.多態性

多態性是指對于不同的類,可以有同名的兩個(或多個)方法。取決于這些方法分別應用到哪個類。例如,定義一個“汽車”類和一個“自行車”類,二者都可以具有不同的“移動”操作。多態性增強了軟件的靈活性和重用性。

6.2 PHP與對象

視頻講解

6.2.1 類的定義

和很多面向對象的語言一樣,PHP也是通過class關鍵字加類名來定義類的。類的格式如下:

上述兩個大括號中間的部分是類的全部內容,如上述SportObject就是一個最簡單的類。SportObject類僅有一個類的骨架,什么功能都沒有實現,但這并不影響它的存在。

注意

一個類,即一對大括號之間的全部內容都要在一段代碼段中,即一個“<?php …?>”之間不能分割成多塊,例如,下面的格式是不允許的:

6.2.2 成員方法

類中的函數被稱為成員方法。函數和成員方法唯一的區別就是,函數實現的是某個獨立的功能,而成員方法是實現類中的一個行為,是類的一部分。

下面就創建在圖6.1中編寫的運動類,并添加成員方法。將類命名為SportObject,并添加打籃球的成員方法beatBasketball()。代碼如下:

該方法的作用是輸出申請打籃球人的基本信息,包括姓名、身高和年齡。這些信息是通過方法的參數傳進來的。

6.2.3 類的實例化

定義完類和方法后,并不會真正創建一個對象。這有點像一輛汽車的設計圖。設計圖可以告訴我們汽車長什么樣,但設計圖本身不是一輛汽車。我們不能開走它,它只能用來建造真正的汽車,而且可以使用它制造很多汽車。那么如何創建對象呢?

首先要對類進行實例化,實例化是通過關鍵字new來聲明一個對象。然后使用如下格式來調用要使用的方法:

在6.1節中已經講過,類是一個抽象的描述,是功能相似的一組對象的集合。如果想用到類中的方法或變量,首先就要把它具體落實到一個實體,也就是對象上。

以SportObject類為例,實例化一個對象并調用playBasketball()方法。代碼如下:

運行結果如下:

6.2.4 成員變量

類中的變量,也稱為成員變量(也有稱為屬性或字段的)。成員變量用來保存信息數據,或與成員方法進行交互來實現某項功能。例如,在SportObject類中定義一個name(運動員姓名)成員變量,接下來就可以在playBasketball()方法中使用該變量完成某個功能。

定義成員變量的格式如下:

說明

關鍵字可以使用public、private、protected、static和final中的任意一個。在6.2.9節之前,所有的實例都使用public來修飾。對于關鍵字的使用,將在6.2.9節中進行介紹。

訪問成員變量和訪問成員方法是一樣的。只要把成員方法換成成員變量即可,格式如下:

【例6.01】 以圖6.1和圖6.2中描述的類和類的實例化為例,將其通過代碼實現。首先定義運動類SportObject,聲明3個成員變量$name、$height、$weight。然后定義一個成員方法playFootball(),用于判斷申請的運動員是否適合這個運動項目。最后,實例化類,通過實例化返回對象調用指定的方法,根據運動員填寫的參數,判斷申請是否符合要求。代碼如下:(實例位置:資源包\源碼\06\6.01)

運行結果如圖6.4所示。

圖6.4 實例化類運行效果

說明

“$this->”作用是調用本類中的成員變量或成員方法,這里只要知道含義即可。在6.2.8 節中將介紹相關的知識。

注意

無論是使用“$this->”還是使用“對象名->”的格式,后面的變量是沒有$符號的,如$this-??> beatBasketBall、$sport->beatBasketBall。

6.2.5 類常量

既然有變量,當然也會有常量。常量就是不會改變的量,是一個恒值。圓周率是眾所周知的一個常量。定義常量使用關鍵字const,如:

例如,先聲明一個常量,再聲明一個變量,實例化對象后分別輸出兩個值。代碼如下:

運行結果如下:

可以發現,常量的輸出和變量的輸出是不一樣的。常量不需要實例化對象,直接由“類名+常量名”調用即可。常量輸出的格式如下:

說明

類名和常量名之間的兩個冒號“::”稱為作用域操作符,使用這個操作符可以在不創建對象的情況下調用類中的常量、變量和方法。關于作用域操作符,將在6.2.8節中進行介紹。

6.2.6 構造方法和析構方法
1.構造方法

當一個類實例化一個對象時,可能會隨著對象初始化一些成員變量。

說明

初始化表示“開始時做好準備”。在軟件開發中對某個東西初始化時,就是把它設置成一種我們期望的形狀或條件,以備使用。

如例6.01中的SportObject類,現在再添加一些成員變量,類的形式如下:

實例化一個SportObject類的對象,并對這個類的一些成員變量賦初值。代碼如下:

可以看到,如果賦初值比較多,寫起來就比較麻煩。為此,PHP引入了構造方法。構造方法是生成對象時自動執行的成員方法,作用就是初始化對象。該方法可以沒有參數,也可以有多個參數。構造方法的格式如下:

注意

函數中的“__”是兩條下畫線“_”。

例如,重寫了SportObject類和playFootBall()方法,下面通過具體實例查看重寫后的對象在使用上有哪些不一樣。代碼如下:

運行結果如下:

可以看到,重寫后的類,在實例化對象時只需一條語句即可完成賦值。

說明

構造方法是初始化對象時使用的。如果類中沒有構造方法,那么PHP會自動生成。自動生成的構造方法沒有任何參數,沒有任何操作。

2.析構方法

析構方法的作用和構造方法正好相反,是在對象被銷毀時調用,作用是釋放內存。析構方法的格式如下:

例如,首先聲明一個對象$sport,然后再銷毀對象。可以看出,使用析構方法十分簡單。代碼如下:

運行結果如下:

說明

PHP使用的是一種“垃圾回收”機制,自動清除不再使用的對象,釋放內存。也就是說即使不使用unset()函數,析構方法也會自動被調用,這里只是明確一下析構方法在何時被調用。一般情況下是不需要手動創建析構方法的。

6.2.7 繼承和多態

繼承和多態最根本的作用就是完成代碼的重用。下面就來介紹PHP的繼承和多態。

1.繼承

子類可以繼承父類的所有成員變量和方法,包括構造方法。當子類被創建時,PHP會先在子類中查找構造方法。如果子類有自己的構造方法,PHP會先調用子類中的方法。當子類中沒有時,PHP則去調用父類中的構造方法,這就是繼承。

例如,在6.1節中通過圖片展示了一個運動類,在這個運動類中包含很多個方法,代表不同的體育項目,各種體育項目的方法中有公共的屬性。例如,姓名、性別、年齡……但還會有許多不同之處,例如,籃球對身高的要求、舉重對體重的要求……如果都由一個SportObject類來生成各個對象,除了那些公共屬性外,其他屬性和方法則需自己手動來寫,工作效率得不到提高。這時,可以使用面向對象中的繼承來解決這個難題。

下面來看如何通過PHP中的繼承來解決上述問題。繼承是通過關鍵字extends來聲明的,繼承的格式如下:

說明

subClass為子類名稱,superClass為父類名稱。

【例6.02】 使用SportObject類生成了兩個子類:PlayBasketBall和WeightLifting,兩個子類使用不同的構造方法實例化了兩個對象Playbasketball和weightlifting,并輸出信息。代碼如下:(實例位置:資源包\源碼\06\6.02)

運行結果如圖6.5所示。

圖6.5 繼承父類運行結果

2.多態

多態好比有一個成員方法讓大家去游泳,這個時候有的人帶游泳圈,還有人拿浮板,還有人什么也不帶。雖是同一種方法,卻產生了不同的形態,這就是多態。

例如,定義一個汽車抽象類Car,它有一個獲取速度的成員方法getSpeed()。現在有3個汽車品牌的子類,分別繼承Car父類,并且都有一個獲取速度的成員方法getSpeed()。3個不同子類,調用同一個方法,將產生3種不同的形態,代碼如下:

運行結果如下:

6.2.8 “$this->”和“::”的使用

通過例6.02可以發現,子類不僅可以調用自己的變量和方法,也可以調用父類中的變量和方法。那么對于其他不相關的類成員同樣可以調用。

PHP是通過偽變量“$this->”和作用域操作符“::”來實現這些功能的,這兩個符號在前面的學習中都有過簡單的介紹。本節將詳細講解兩者的使用。

1.$this->

在6.2.3節中,對如何調用成員方法有了基本的了解,即使用對象名加方法名,格式為“對象名->方法名”。但在定義類時(如SportObject類),根本無法得知對象的名稱是什么。這時如果想調用類中的方法,就要用偽變量“$this ->”。“$this”的意思就是本身,所以“$this->”只可以在類的內部使用。

例如,當類被實例化后,“$this”同時被實例化為本類的對象,這時對“$this”使用get_class()函數,將返回本類的類名。代碼如下:

運行結果如下:

說明

get_class()函數返回對象所屬類的名字,如果不是對象,則返回false。

2.操作符“::”

相比偽變量“$this”只能在類的內部使用,操作符“::”更為強大。操作符“::”可以在沒有聲明任何實例的情況下訪問類中的成員方法或成員變量。使用“::”操作符的通用格式如下:

這里的關鍵字分為以下3種情況。

parent關鍵字:可以調用父類中的成員變量、成員方法和常量。

self關鍵字:可以調用當前類中的靜態成員和常量。

類名:可以調用本類中的變量、常量和方法。

例如,依次使用了類名、parent關鍵字和self關鍵字來調用變量和方法。讀者可以觀察輸出的結果。代碼如下:

運行結果如下:

說明

關于靜態變量(方法)的聲明及使用可參考6.2.10節相關內容。

6.2.9 數據隱藏

細心的讀者看到這里,一定會有一個疑問:面向對象編程的特點之一是封裝性,即數據隱藏。但是在前面的學習中并沒有突出這一點。對象中的所有變量和方法可以隨意調用,甚至不用實例化也可以使用類中的方法、變量。這就是面向對象嗎?

這當然不算是真正的面向對象。如果讀者是從本章第一節來開始學習的,一定還會記得在6.2.4節講成員變量時所提到的那幾個關鍵字:public、private、protected、static和final。這就是用來限定類成員(包括變量和方法)的訪問權限的。本節先來學習前3個。

說明

成員變量和成員方法在關鍵字的使用上都是一樣的。這里只以成員變量為例說明幾種關鍵字的不同用法。對于成員方法同樣適用。

1.public(公共成員)

顧名思義,就是可以公開的、沒有必要隱藏的數據信息。可以在程序中的任何位置(類內、類外)被其他的類和對象調用。子類可以繼承和使用父類中所有的公共成員。

在本章的前半部分,所有的變量都被聲明為public,而所有的方法在默認狀態下也是public。所以對變量和方法的調用顯得十分混亂。為了解決這個問題,就需要使用第二個關鍵字private。

2.private(私有成員)

被private關鍵字修飾的變量和方法,只能在所屬類的內部被調用和修改,不可以在類外被訪問。在子類中也不可以。

例如,對私有變量$name的修改與訪問,只能通過調用成員方法來實現。如果直接調用私有變量,將會發生錯誤。代碼如下:

運行結果如圖6.6所示。

圖6.6 private關鍵字

說明

對于成員方法,如果沒有寫關鍵字,那么默認就是public。從本節開始,以后所有的方法及變量都會帶上關鍵字,這是一種良好的書寫習慣。

3.protected(保護成員)

private關鍵字可以將數據完全隱藏起來,除了在本類外,其他地方都不可以調用,子類也不可以。對于有些變量希望子類能夠調用,但對另外的類來說,還要做到封裝。這時,就可以使用protected。

說明

被protected修飾的類成員,可以在本類和子類中被調用,其他地方則不可以被調用。

例如,聲明一個protected變量,然后使用子類中的方法調用一次,最后在類外直接調用一次,觀察一下運行結果。代碼如下:

運行結果如圖6.7所示。

圖6.7 protected關鍵字運行結果

說明

雖然PHP中沒有對修飾變量的關鍵字做強制性的規定和要求,但從面向對象的特征和設計方面考慮,一般使用private或protected關鍵字來修飾變量,以防止變量在類外被直接修改和調用。

6.2.10 靜態變量(方法)

不是所有的變量(方法)都需要通過創建對象來調用。可以通過給變量(方法)加上static關鍵字來直接調用。調用靜態成員的格式如下:

關鍵字可以是:

self,在類內部調用靜態成員時所使用。

靜態成員所在的類名,在類外調用類內部的靜態成員時所使用。

注意

在靜態方法中,只能調用靜態變量,而不能調用普通變量,而普通方法則可以調用靜態變量。

使用靜態成員,除了可以不需要實例化對象,另一個作用就是在對象被銷毀后,仍然保存被修改的靜態數據,以便下次繼續使用。這個概念比較抽象,下面結合一個實例說明。

首先聲明一個靜態變量$num,聲明一個方法,在方法的內部調用靜態變量,然后給變量加1。依次實例化這個類的兩個對象,并輸出方法。可以發現兩個對象中的方法返回的結果有了一些聯系。直接使用類名輸出靜態變量,看有什么效果。代碼如下:

運行結果如下:

如果將程序代碼中的靜態變量改為普通變量,如“private $num=0;”,那么結果就不一樣了。讀者可以動手試一試。

說明

靜態成員不用實例化對象,當類第一次被加載時就已經分配了內存空間,所以直接調用靜態成員的速度要快一些。但如果靜態成員聲明得過多,空間一直被占用,反而會影響系統的功能。這個尺度只能通過實踐積累,才能真正地掌握。

6.3 小結

本章主要介紹了面向對象的概念、特點和面向對象的應用。雖然本章關于面向對象概念介紹得很全面、很詳細,但要想真正明白面向對象思想,必須要多動手實踐,多動腦思考,注意平時積累等。希望讀者通過自己的努力能有所突破。

6.4 實戰

6.4.1 調用類的成員方法

實例位置:資源包\源碼\06\實戰\01

試著創建一個商品類Goods,聲明一個成員變量$ids(商品id數組)。然后定義一個成員方法searchGoods(),用于查找某個商品id是否存在于商品數組$ids中。運行結果如圖6.8所示。

圖6.8 實例運行結果

6.4.2 生成圖片驗證碼

實例位置:資源包\源碼\06\實戰\02

試著創建ValidateCode類,用于生成圖片驗證碼,運行結果如圖6.9所示。

圖6.9 生成圖片驗證碼

主站蜘蛛池模板: 大田县| 大宁县| 夏邑县| 茶陵县| 盐源县| 鄂托克前旗| 汝南县| 夹江县| 巴彦淖尔市| 南康市| 铜鼓县| 苏尼特右旗| 贵德县| 南靖县| 安龙县| 思茅市| 玉溪市| 舟曲县| 裕民县| 天水市| 电白县| 大庆市| 如皋市| 虹口区| 天气| 威远县| 邳州市| 嘉义县| 阿坝县| 保德县| 泰宁县| 紫金县| 修文县| 改则县| 铜陵市| 建瓯市| 丰城市| 于都县| 淳化县| 铜鼓县| 武山县|