新聞中心
小編給大家分享一下PHP面向?qū)ο筇匦杂心男嘈糯蟛糠秩硕歼€不怎么了解,因此分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后大有收獲,下面讓我們一起去了解一下吧!
創(chuàng)新互聯(lián)2013年開創(chuàng)至今,先為大慶等服務(wù)建站,大慶等地企業(yè),進行企業(yè)商務(wù)咨詢服務(wù)。為大慶企業(yè)網(wǎng)站制作PC+手機+微官網(wǎng)三網(wǎng)同步一站式服務(wù)解決您的所有建站問題。
面向?qū)ο蟮娜齻€主要特性是封裝、繼承和多態(tài)。
創(chuàng)建對象
$對象名 = new 類名(); $對象名 = new 類名;
- new 是一個關(guān)鍵字,表示創(chuàng)建一個新的實例。
- 在類定義內(nèi)部,可以用
new self
和new parent
創(chuàng)建新對象。
成員屬性
成員屬性必須要有訪問修飾符,如果不寫,會報錯。
成員屬性的默認值為NULL。
示例:
class Test { public $name; //共有 protected $height; //保護 private $age; //私有 }
成員方法
成員方法如果不寫訪問修飾符,則默認為public。
我們可以指定成員方法里的參數(shù)的類型,如 類類型,或者數(shù)組類型(array),也可以是接口類型,但是接口類型要注意,因為接口沒有實例,當我們聲明參數(shù)類型為接口類型的時候,那么傳入的參數(shù)只要是實現(xiàn)了該接口的類的實例即可。
示例:
class Test { function hello() {} //默認為public public function say() {} //公有 protected function smile() {} //保護 private function laugh() {} //私有 }
構(gòu)造方法
- 一個類里只能有一個構(gòu)造方法。
- 沒有返回值(即使在構(gòu)造方法里寫了返回值也沒有意義)
- 在創(chuàng)建一個類的新對象時,系統(tǒng)會自動的調(diào)用類的構(gòu)造方法完成對新對象的初始化。
- 如果我們沒有重寫默認構(gòu)造函數(shù),那么系統(tǒng)會默認幫我們創(chuàng)建一個沒有參數(shù)的構(gòu)造函數(shù),
public function __construct(){}
,如果我們重寫了構(gòu)造函數(shù),那么我們自定義的構(gòu)造函數(shù)就會覆蓋系統(tǒng)默認的構(gòu)造函數(shù)。 - 如果我們自定義的構(gòu)造函數(shù)里有行參,那么當我們創(chuàng)建對象的時候就必須傳入相應(yīng)的實參。否則會報錯。
示例:
class Test { public function __construct() { echo 'hello, world
'; } } $testObj = new Test(); class One { public function __construct($param) { echo $param; } } $oneObj = new One('hello, world
');
輸出結(jié)果:
hello, world hello, world
析構(gòu)方法
- 當一個對象沒有被任何變量引用時就會自動銷毀,并在被銷毀前自動調(diào)用析構(gòu)函數(shù),無論程序有沒有結(jié)束,只要一個對象沒有被任何變量引用就會被銷毀。
- 當PHP程序運行結(jié)束后,所有的對象都會被銷毀,對象在被銷毀前,會自動調(diào)用析構(gòu)函數(shù)。
- 默認情況下,析構(gòu)函數(shù)銷毀對象的順序和創(chuàng)建對象的順序正好相反。最先創(chuàng)建的對象最后被銷毀,強調(diào)默認情況下是因為,我們可以手動銷毀對象,這樣的話對象銷毀的順序就和默認情況不一樣了,當PHP程序執(zhí)行完后,那時候銷毀對象的方法就是采用的默認方法。
- 如果我們定義的一個類里專門有個屬性是用來連接數(shù)據(jù)庫的,當我們創(chuàng)建了該類的實例對象,并且用該成員屬性連接數(shù)據(jù)庫后,如果該類型的實例對象占用資源較大,我們往往會在使用后就立即銷毀,雖然該對象能被銷毀,但是該對象的成員屬性所連接的資源卻不會被自動銷毀,需要我們自己手動銷毀,這時,我們可以在析構(gòu)函數(shù)里添加一句關(guān)閉數(shù)據(jù)庫連接的語句就好了(MySQL_close(要關(guān)閉的資源))。
示例:
class Test { public function __construct() { echo 'hello, world
'; } public function __destruct() { echo '執(zhí)行一些回收資源的操作
'; } } $testObj = new Test();
輸出結(jié)果:
hello, world //執(zhí)行一些回收資源的操作
垃圾回收機制
- 在PHP中,當一個對象沒有任何引用指向它的時候,就會成為一個垃圾對象,PHP將啟用垃圾回收器將對象銷毀。
- 在PHP程序退出前,PHP也將啟用垃圾回收器,銷毀對象。
訪問修飾符
public
如果沒有為方法指定訪問修飾符,它將是
public
。公有的屬性或方法可以在類的內(nèi)部或外部進行訪問。protected
如果一個屬性或方法指定訪問修飾符為
protected
,那么被標記的屬性或方法只能在類內(nèi)部訪問,被修飾的屬性和方法能被子類繼承。private
如果一個屬性或方法指定訪問修飾符為
private
,那么被標記的屬性或方法只能在類內(nèi)部訪問,私有的屬性和方法將不會被繼承。
魔術(shù)方法
魔術(shù)方法都是在滿足某個條件時,由系統(tǒng)自動調(diào)用。
魔術(shù)方法的名字,都是以兩個下劃線(__)開頭的,因此我們在自定義函數(shù)時,函數(shù)名不要以兩個下劃線開頭。
一些常用的魔術(shù)方法
__construct(): 構(gòu)造方法 __destruct(): 析構(gòu)方法 __set(): 在給不可訪問屬性賦值(比如:protected/private/不存在)時,__set()會被調(diào)用。 __get(): 讀取不可訪問屬性的值(比如:protected/private/不存在)時,__get()會被調(diào)用。 __isset(): 當對不可訪問屬性(比如:protected/private/不存在)調(diào)用isset()或empty()時,__isset()會被調(diào)用。 __unset(): 當對不可訪問屬性(比如:protected/private/不存在)調(diào)用unset()時,__unset()會被調(diào)用。 __toString(): 當我們把一個對象當做字符串輸出時,就會自動調(diào)用__stoString()方法。 __clone(): 如果定義了__clone方法,則新創(chuàng)建的對象clone(復(fù)制生成的對象)中的__clone()會被調(diào)用。用法如下: $copy_of_object = clone $object; 克隆后的對象與原對象的地址不一樣。 當對象被復(fù)制后,PHP 5 會對對象的所有屬性執(zhí)行一個淺復(fù)制(shallow copy)。所有的引用屬性 仍然會是一個指向原來的變量的引用。 如果想防止其他程序員克隆某個對象,可以重寫__clone()方法并把訪問修飾符設(shè)置為private。 __call(): 在對象中調(diào)用一個不可訪問(比如函數(shù)訪問修飾符為protected/private/不存在)方法時,__call()會被調(diào)用。 __callStatic(): 用靜態(tài)方式中調(diào)用一個不可訪問方法時,__callStatic() 會被調(diào)用。
對象比較
當使用比較運算符(==)比較兩個對象變量時,比較的原則是:如果兩個對象的屬性和屬性值 都相等,而且兩個對象是同一個類的實例,那么這兩個對象變量相等。
而如果使用全等運算符(===),這兩個對象變量一定要指向某個類的同一個實例(即同一個對象)。
示例:
class Test { public $name = 'itbsl'; public $age = 25; } $testObj = new Test(); $testObj2 = new Test(); if ($testObj == $testObj2) { echo '相等
'; } else { echo '不相等
'; } $testObj2->name = 'jack'; if ($testObj == $testObj2) { echo '相等
'; } else { echo '不相等
'; }
繼承
即使父類的某些屬性設(shè)置為私有的,子類仍然能夠繼承,但是對子類卻是不可見的。當子類對象使用父類的私有屬性的時候,會自動觸發(fā)重載機制,在子類創(chuàng)建一個和父類私有屬性同名的屬性。此時子類使用的屬性并不是父類的私有屬性了,而是通過重載創(chuàng)建的和父類私有屬性同名的屬性罷了。
如果在子類中需要訪問其父類的構(gòu)造方法( 方法的訪問修飾符是public/protected)可以使用父類::方法名(或者 parent::方法名)來完成。(推薦使用parent::方法名,并且一般調(diào)用父類構(gòu)造方法都是parent::__construct();)
調(diào)用父類的普通方法直接用this->方法名即可。(也可以用父類名::方法名或者 parent::方法名 ,但是不推薦)
繼承并不是直接把父類的屬性和方法直接拷貝到子類里面來。而是建立了一種關(guān)聯(lián)。原本我以為是直接把父類的屬性和方法直接拷貝過來,最后發(fā)現(xiàn)只是建立了一種關(guān)聯(lián)。下圖證明了不是直接拷貝,當在子類里調(diào)用父類的方法時,父類輸出自己的屬性時輸出結(jié)果都和初始化不一樣了(private屬性除外,因為是私有的,子類無法看見,所以子類也無法重寫父類的私有屬性),如果是直接拷貝的話,那么當調(diào)用父類的方法時應(yīng)該輸出100,200,300才對,所以說繼承不是直接拷貝。
class A { public $num1 = 100; protected $num2 = 200; private $num3 = 300; public function show1() { echo 'num1 = ' . $this->num1 . '
'; } public function show2() { echo 'num2 = ' . $this->num2 . '
'; } public function show3() { echo 'num3 = ' . $this->num3 . '
'; } } class B extends A { public $num1 = 1; protected $num2 = 2; private $num3 = 3; public function show1() { echo 'num1 = ' . $this->num1 . '
'; parent::show1(); } public function show2() { echo 'num2 = ' . $this->num2 . '
'; parent::show2(); } public function show3() { echo 'num3 = ' . $this->num3 . '
'; parent::show3(); } } $bObj = new B(); $bObj->show1(); $bObj->show2(); $bObj->show3();
輸出結(jié)果:
num1 = 1 num1 = 1 num2 = 2 num2 = 2 num3 = 3 num3 = 300
重載
PHP所提供的"重載"(overloading)是指動態(tài)地"創(chuàng)建"類屬性和方法。我們是通過魔術(shù)方法(magic methods)來實現(xiàn)的。
當調(diào)用當前環(huán)境下未定義或不可見的類屬性或方法時,重載方法會被調(diào)用。本節(jié)后面將使用"不可訪問屬性(inaccessible properties)"和"不可訪問方法(inaccessible methods)"來稱呼這些未定義或不可見的類屬性或方法。
屬性重載只能在對象中進行。在靜態(tài)方法中,這些魔術(shù)方法將不會被調(diào)用。所以這些方法都不能被聲明為 static。從 PHP5.3.0 起, 將這些魔術(shù)方法定義為 static 會產(chǎn)生一個警告。
所有重載方法都必須被聲明為public。
這些魔術(shù)方法都參數(shù)都不能通過引用傳遞。
PHP中的"重載"與其它絕大多數(shù)面向?qū)ο笳Z言不同。傳統(tǒng)的"重載"是用于提供多個同名的類方法,但各方法的參數(shù)類型和個數(shù)不同。
因為PHP處理賦值運算的方式,__set()的返回值將被忽略。類似的,在下面這樣的鏈式賦值中,__get()不會被調(diào)用。
$a = $obj->b = 8;
在除 isset() 外的其它語言結(jié)構(gòu)中無法使用重載的屬性,這意味著當對一個重載的屬性使用 empty() 時,重載魔術(shù)方法將不會被調(diào)用。
為避開此限制,必須將重載屬性賦值到本地變量再使用 empty()。
//在對象中調(diào)用一個不可訪問方法時,__call() 會被調(diào)用。 public mixed __call(string $name , array $arguments) //用靜態(tài)方式中調(diào)用一個不可訪問方法時,__callStatic() 會被調(diào)用。 public static mixed __callStatic(string $name , array $arguments)
屬性重載
我們曾經(jīng)提過,當我們訪問或給一個不可訪問(protected/private)的屬性或者不存在的屬性賦值時,就會調(diào)用相應(yīng)的系統(tǒng)魔術(shù)方法__get($property)
、__set($property, $value)
,如果我們不重寫這兩個方法,當我們給不存在的屬性賦值時,系統(tǒng)會自動幫我們創(chuàng)建一個public的屬性,我們可以自己重寫這兩個方法來管理這些這些動態(tài)創(chuàng)建的屬性?;蛘咧苯硬蛔尞a(chǎn)生動態(tài)屬性。當我們不重寫這兩個方法就不可訪問屬性賦值時,會報致命錯誤。
示例:
class Obj { protected $name = 'itbsl'; } $obj = new Obj(); $obj->name = "jack"; //此處會報致命錯誤 var_dump($obj);
錯誤信息為:
Fatal error: Uncaught Error: Cannot access protected property Obj::$name
當我們重寫了__set()
方法就沒問題了
class Obj { protected $str = 'itbsl'; public function __set($name, $value) { $this->$name = $value; } } $obj = new Obj(); $obj->str = "jack"; var_dump($obj);
屬性重載可以幫助我們動態(tài)管理新的屬性也可以禁止動態(tài)創(chuàng)建屬性。
(1)動態(tài)管理
class Dog { //定義一個數(shù)組,管理我們動態(tài)增加的屬性和值 private $arr = []; //這里我們重寫__set來管理動態(tài)增加的屬性 public function __set($name, $value) { $this->arr[$name] = $value; } public function __get($name) { return isset($this->arr[$name]) ? $this->arr[$name] : null; } }
(2)禁止創(chuàng)建動態(tài)屬性。重寫set方法,里面什么也不做。
class Dog { //這里我們重寫__set來管理動態(tài)增加的屬性 public function __set($name, $value) { //just do nothing } }
方法重寫
在子類中重寫父類的方法時要,重寫的方法的訪問控制符不能比父類的方法的訪問控制符的級別小。例如:如果父類的訪問控制符為public,則子類重寫方法的訪問修飾符只能為public,如果父類的為protected,則子類的訪問修飾控制符可以為protected或public。重寫屬性也要遵守上面的規(guī)則。
屬性重寫
如果子類有和父類相同的屬性,如果屬性是public或者protected則會重寫父類的屬性,如果是private則創(chuàng)建一個同名的新私有屬性,同時仍然會繼承父類的同名私有屬性。
靜態(tài)屬性
- 靜態(tài)屬性不屬于某個對象,而是所有對象共享的屬性,每個對象都可以訪問它。
- 靜態(tài)屬性屬于類的范疇,而不是某個對象的獨有特性。
- 在類中,使用和訪問靜態(tài)變量的方式是
self::$靜態(tài)屬性
。 - 在類外,使用和訪問靜態(tài)變量的方式是
類名::$靜態(tài)屬性
(要求訪問修飾符為public)。 - 當我們用var_dump()輸出一個對象的時候,該對象的靜態(tài)變量不會被輸出。
- 就像其它所有的 PHP 靜態(tài)變量一樣,靜態(tài)屬性只能被初始化為文字或常量,不能使用表達式。所以可以把靜態(tài)屬性初始化為整數(shù)或數(shù)組,但不能初始化為另一個變量或函數(shù)返回值,也不能指向一個對象。
靜態(tài)方法
- 靜態(tài)方法的訪問方式為
類名::靜態(tài)方法名()
; 同時也可以用對象名->靜態(tài)方法名()
;和對象名::靜態(tài)方法名()
,但是后兩種不推薦,盡量只用第一種。 - 在類的外部調(diào)用靜態(tài)方法,要求靜態(tài)方法的訪問修飾符必須是public的。
- 在類內(nèi)部調(diào)用靜態(tài)方法:
self::靜態(tài)方法
或者類名::靜態(tài)方法
通過$this
也可以。只推薦第一種方式。在類的內(nèi)部訪問靜態(tài)方法,無論是什么修飾符都可以訪問靜態(tài)方法。 - 靜態(tài)方法中不可以訪問非靜態(tài)屬性和非靜態(tài)方法。
- 普通的成員方法,可以訪問靜態(tài)屬性。
靜態(tài)屬性和普通屬性的區(qū)別:
(1)加上static稱靜態(tài)變量,否則就是普通屬性
(2)靜態(tài)屬性是與類相關(guān)的,所有對象共享的屬性
(3)普通屬性屬于每個對象個體的屬性。
多態(tài)
由于PHP變量沒有類型限制,所以PHP是天生支持多態(tài)的。
類型約束
類型約束支持的類型有 array 、 callable 、 對象類型 、 接口
抽象類
當父類的一些方法不能確定時,可以用abstract關(guān)鍵字來修飾該方法[抽象方法],用abstract來修飾該類[抽象類]。
(1) 如果你希望把某個方法做成 抽象方法 ,則前面寫上 abstract
(2) 如果一個類中有抽象方法,那么該類必須聲明為抽象類。
(3) 抽象類最主要的作用在于設(shè)計,它的目的是讓其它的類繼承它,并實現(xiàn)其中的抽象方法。如果子類繼承了該抽象類,除非繼承該抽象類的子類也被聲明為抽象類,否則必須實現(xiàn)抽象類中所有的抽象方法,如果不實現(xiàn)就會報錯。
(4) 抽象類不能被實例化
(5) 抽象類可以沒有abstract方法
(6) 抽象類可以有非抽象方法,成員屬性和常量
(7) 抽象方法不能有函數(shù)體
基本語法:
abstract class 類名 { abstract 修飾符 function 函數(shù)名(參數(shù)列表); }
普通類如何繼承抽象類?
abstract class Superman { public $name; public $age; public function __construct($name, $age) { $this->name = $name; $this->age = $age; } abstract public function run(); abstract public function fly(); abstract public function attach(); } class Spiderman extends Superman { public function run() { echo 'Spiderman is running on the net.
'; } public function fly() { echo 'Spiderman can hang in the sky through net.
'; } public function attach() { echo 'Spider attach.
'; } }
接口
(1) 接口就是給出一些沒有實現(xiàn)的方法,封裝到一起,到某個類要使用的時候,再根據(jù)具體情況把這些方法寫出來。
(2)基本語法:
interface 接口名 { //方法[不含方法體] }
(3)接口中所有的方法都不能有主體
(4)一個類可以實現(xiàn)多個接口,接口名之間用逗號隔開
class 類名 implements 接口1, 接口2 { }
(5)接口中可以有屬性,但只能是常量,并且是公開可訪問的。默認是public,但不能用public顯式修飾
(6)接口中的方法都必須是public的,默認就是public
(7)一個接口不能繼承其它的類,但是可以繼承別的接口,而且一個接口可以繼承多個接口
interface 接口名 extends 接口1, 接口2 { //方法[不含方法體] }
(8)接口不能被實例化
(9)接口是更加抽象的抽象類,接口里的所有方法都沒有方法體。接口體現(xiàn)了程序設(shè)計的多態(tài)和高內(nèi)聚低耦合的設(shè)計思想。
(10)說明:
接口的命名規(guī)范一般是字符 i 開頭,然后第二個字符大寫,形式如:iXxxx,比如:iUsb
(11)如何使用接口中的常量
接口名::常量名;
如果某個類要實現(xiàn)接口,需要用implements 關(guān)鍵字。并且實現(xiàn)接口里的所有方法,如果該類要實現(xiàn)多個接口,則所有的接口的所有的方法都要實現(xiàn),只要存在沒有實現(xiàn)的接口里的方法就會報錯。
示例:
interface Displayable { function display(); } class Base { function operation() { echo 'operate something.
'; } } class SubClass extends Base implements Displayable { function display() { echo 'display.
'; } } $temp = new SubClass(); $temp->display();
final關(guān)鍵字
(1) 作用:
因為安全的考慮,類的某個方法不允許子類通過重寫來修改。
不希望某個類被其它的類繼承。
(2) PHP5新增了一個final關(guān)鍵字。如果父類中的方法被聲明為final,則子類無法覆蓋該方法。如果一個類被聲明為final,則該類不能被繼承。
(3) final不能夠修飾成員屬性(變量)
(4) final方法不能被重寫,但可以被繼承。即使是被聲明為final的方法也依然能夠被繼承并被使用,只是不能重寫(修改)罷了。
(5) 一般來說,final類中不會出現(xiàn)final方法,因為final類都不能被繼承,也就不會去重寫override final類的方法了。
(6) final類是可以被實例化的。
類常量
可以把在類中始終保持不變的值定義為常量。在定義和使用常量的時候不需要使用 $ 符號。
常量的值必須是一個定值,不能是變量、類屬性、數(shù)學運算的結(jié)果或函數(shù)調(diào)用。
接口(interface)中也可以定義常量。更多示例見文檔中的接口部分。
自 PHP 5.3.0 起,可以用一個變量來動態(tài)調(diào)用類。但該變量的值不能為關(guān)鍵字(如 self,parent 或 static)。
細節(jié)說明:
(1) 常量名一般字母全部大寫:TAX_RATE,中間可以有下劃線
(2) 在定義常量的同時,必須賦初值,比如 const TAX_RATE = 1.1
(3) const關(guān)鍵字前不能用public/protected/private修飾。默認是public
(4) 訪問常量
在類的外部 類名::常量名
接口名::常量名
在類的內(nèi)部 類名::常量名
self::常量名
(5) 常量的值在定義的時候就初始化,以后就不能修改
(6) 常量可以被子類繼承
(7) 一個常量是屬于一個類的,而不是某個對象的。
(8) 常量是全局性的,可以在任何地方訪問。
(9) 在類里面不能用define定義常量。
(10) 常量的值可以是基本類型數(shù)據(jù)和數(shù)組。不可以是對象。
對象遍歷
foreach用法和之前的數(shù)組遍歷是一樣的,只不過這里遍歷的key是屬性名,value是屬性值。在類外部遍歷時,只能遍歷到public屬性的,因為其它的都是受保護的,類外部不可見。
示例:
class HardDiskDrive { public $brand; public $color; public $cpu; public $workState; protected $memory; protected $hardDisk; private $price; public function __construct($brand, $color, $cpu, $workState, $memory, $hardDisk, $price) { $this->brand = $brand; $this->color = $color; $this->cpu = $cpu; $this->workState = $workState; $this->memory = $memory; $this->hardDisk = $hardDisk; $this->price = $price; } } $hardDiskDrive = new HardDiskDrive('希捷', 'silver', 'tencent', 'well', '1T', 'hard', '$456'); foreach ($hardDiskDrive as $property => $value) { var_dump($property, $value); echo '
'; }
輸出結(jié)果為:
string(5) "brand" string(6) "希捷" string(5) "color" string(6) "silver" string(3) "cpu" string(7) "tencent" string(9) "workState" string(4) "well"
如果我們想遍歷出對象的所有屬性,就需要控制foreach的行為,就需要給類對象,提供更多的功能,需要繼承自Iterator的接口:
該接口,實現(xiàn)了foreach需要的每個操作。foreach的執(zhí)行流程如下圖:
http://fisionsoft.com.cn/article/jggdos.html