- PHP從入門到精通(微視頻精編版)
- 明日科技
- 6219字
- 2020-11-23 14:41:16
第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 生成圖片驗證碼
- 演進式架構(原書第2版)
- Python 3網絡爬蟲實戰
- Scratch 3.0少兒編程與邏輯思維訓練
- Java EE 7 Development with NetBeans 8
- PHP+MySQL+Dreamweaver動態網站開發實例教程
- Mastering Linux Network Administration
- Java程序設計
- Python編程從0到1(視頻教學版)
- Visual Basic程序設計(第三版)
- 深度探索Go語言:對象模型與runtime的原理特性及應用
- Visual Basic語言程序設計基礎(第3版)
- Drupal 8 Development Cookbook(Second Edition)
- ASP.NET本質論
- 信息學競賽寶典:基礎算法
- Cocos2D Game Development Essentials