物件導向
物件導向基礎
物件導向
什麼是類?
具有相同屬性(特徵)和方法(行為)的一系列個體的集合,類是一個抽象的概念。
什麼是物件?
從類中,拿到的具有具體屬性值的個體,稱為物件。物件是一個具體的個體。
eg:人類;張三
類和物件的關係?
- 類是物件的抽象化!物件是類的具體化!
- 類僅僅表明這類物件有哪些屬性,但是不能有具體的值,所以類是抽象的。
- 物件是將類的所有屬性賦值後,產生具體的個體,所有物件是具體的。
類的宣告與例項化
如何宣告一個類:
class 類名{
訪問修飾符 $屬性[=預設值];
[訪問修飾符] function 方法(){}
}
class SimpleClass {
// property declaration
public $var = `a default value`;
// method declaration
public function displayVar() {
echo $this->var;
}
}
宣告一個類的注意事項:
- 類名只能有字母數字下劃線組成,開頭不能是數字,必須符合大駝峰法則;
- 類名必須使用
class
修飾,類名後面一定不能有(); - 屬性必須要帶訪問修飾符,方法可以不帶訪問修飾符。
例項化物件及物件屬性方法的呼叫:
$物件名 = new 類名(); //()可以不帶
類外部呼叫屬性和方法:
$物件名 -> $屬性名; //使用->呼叫屬性時,屬性名不能帶$符號
類內部呼叫屬性和方法:
$this -> $屬性名;
建構函式
什麼是建構函式?
建構函式是類中的一個特殊函式,當我們使用new關鍵字例項化物件時,相當於呼叫了類的建構函式。
建構函式有什麼作用?
例項化物件時,自動呼叫,用於給物件的屬性賦初值!
建構函式的寫法:
- 建構函式名,必須與類同名(廢棄)
[public] function Person($name){
$this -> name = $name;
}
- 使用魔術方法
__construct
[public] function __construct($name){
$this -> name = $name;
}
建構函式注意事項:
- 第一種寫法,建構函式名必須與類同名!!!!
- 如果一個類沒有手寫建構函式,則系統預設會有一個空參構造,因此可以使用
new Person()
; - 如果我們寫了帶引數的建構函式,則將不會再有空參構造,也就是不能直接使用
new Person()
; -
Person
後面的()
中的引數列表,必須符合建構函式的要求!!!! - 如果兩種建構函式同時存在,將使用
__construct
。
解構函式:__destruct()
:
- 解構函式在物件被銷燬釋放之前自動呼叫;
- 解構函式不能帶有任何的引數;
- 解構函式常用於物件使用完以後,釋放資源,關閉資源等。
魔術方法:
PHP中,給我們提供一系列用__
開頭的函式,這些函式無需自己手動呼叫,
會在合適的時機自動呼叫,這類函式稱為魔術稱為魔術函式。
eg:function __construct(){}
在類new一個物件時自動呼叫function __destruct(){}
在物件被銷燬時自動呼叫,我們要求,除了魔術方法之外,自定義的函式與方法不能使用__
開頭。最後,一般對於功能比較複雜的類,我們會單獨的寫到一個類檔案中。類檔案的命名,同一小寫,使用"類名小寫.class.php"
的方式命名。在其他檔案中使用這個類時,可以使用include匯入這個".class.php"
檔案。
封裝和繼承
什麼是封裝?
通過訪問修飾符,將類中不需要外部訪問的屬性和方法進行私有化處理,以實現訪問控制。
注意:是實現訪問控制,而不是拒絕訪問。也就是說,我們私有化屬性後,需要提供對應的方法,讓使用者通過我們提供的方法處理屬性。
封裝的作用?
- 使用者只關心類能夠提供的功能,不關心功能實現的細節!(封裝方法)
- 對使用者的資料進行控制,防止設定不合法資料,控制返回給使用者的資料(屬性封裝+
set/get
方法)
實現封裝操作?
方法的封裝
對於一些只在類內部使用的方法,而不像對外部提供使用,那麼,這樣的方法我們可以使用private進行私有化處理。
private function formatName(){} //這個方法僅僅能在類內部使用$this呼叫
function showName(){
$this->formatName();
}
屬性的封裝+set/get
方法
為了控制屬性的設定以及讀取,可以將屬性進行私有化處理,並要求使用者通過我們提供的set/get
方法進行設定
private $age;
//set方法
function setAge($age){
$this->age=$age;
}
//get方法
function getAge(){
return $this->age;
}
$物件->getAge();
$物件->setAge(12);
屬性的封裝+魔術方法
__get( -> __set(, ->= }
$物件->age; //訪問物件私有屬性時,自動呼叫__get()魔術方法,並且將訪問的屬性名傳給__get()方法;
$物件->age=12; //設定物件私有屬性時,自動呼叫__set()魔術方法,並且將設定的屬性名以及屬性值傳給__set()方法;
注意:在魔術方法中,可以使用分支結構,判斷$key的不同,進行不同操作。
關於封裝的魔術方法:
-
__set($key,$value)
:給類私有屬性賦值時自動呼叫,呼叫時給方法傳遞兩個引數:需要設定的屬性名,屬性值。 -
__get($key,$value)
:讀取類私有屬性時自動呼叫,呼叫時給方法傳遞一個引數,需要讀取的屬性名; -
__isset($key)
:外部使用isset()
函式檢測私有屬性時,自動呼叫。
類外部使用isset();檢測私有屬性,預設是檢測不到的。false
所以,我們可以使用__isset();函式,在自動呼叫時,返回內部檢測結果。
function __isset($key){return isset($this -> $key);}
當外部使用isset($物件名->私有屬性);檢測時,將自動呼叫上述__isset()返回的結果!
-
__unset($key)
:外部使用unset()
函式刪除私有屬性時,自動呼叫;
function __unset($key){unset($this -> $key);}
當外部使用unset($物件名->私有屬性);刪除屬性時,自動將屬性名傳給__unset(),並交由這個魔術方法處理。
繼承的基礎知識:
如何實現繼承?
給子類使用extends
關鍵字,讓子類繼承父類;
class Student extends Person{}
現繼承的注意事項?
- 子類只能繼承父類的非私有屬性。
- 子類繼承父類後,相當於將父類的屬性和方法copy到子類,可以直接使用$this呼叫。
- PHP只能單繼承,不支援一個類繼承多個類。但是一個類進行多層繼承。
class Person{}
class Adult extends Person{}
class Student extends Adult{}
//Student 類就同時具有了Adult類和Person類的屬性和方法
方法覆蓋(方法重寫)
- 子類繼承父類
- 子類重寫父類已有方法
符合上述兩個條件,稱為方法覆蓋。覆蓋之後,子類呼叫方法,將呼叫子類自己的方法。
同樣,除了方法覆蓋,子類也可以具有與父類同名的屬性,進行屬性覆蓋。
如果,子類重寫了父類方法,如何在子類中呼叫父類同名方法?
partent::方法名();
所以,當子類繼承父類時,需在子類的構造中的第一步,首先呼叫父類構造進行復制。
function __construct($name,$sex,$school){
partent::__construct($name,$sex);
$this -> school = $school;
}
PHP關鍵字
final
-
final
修飾類,此類為最終類,不能被繼承! -
final
修飾方法,此方法為最終方法,不能被重寫! -
final
不能修飾屬性。
static
- 可以修飾屬性和方法,分別稱為靜態屬性和靜態方法,也叫類屬性,類方法;
- 靜態屬性,靜態方法,只能使用類名直接呼叫。
使用”類名::$靜態屬性” , “類名::靜態方法()”
Person::$sex; Person::say();
- 靜態屬性和方法,在類裝載時就會宣告,先於物件產生。
- 靜態方法中,不能呼叫非靜態屬性或方法;
非靜態方法,可以呼叫靜態屬性和方法。 (因為靜態屬性和方法在類裝載時已經產生,而非靜態的屬性方法,此時還沒有例項化誕生)
- 在類中,可以使用
self
關鍵字,代指本類名。
class Person{
static $sex = "nan";
function say(){
echo self::$sex;
}
}
- 靜態屬性是共享的,也就是new出很多物件,也是共用一個屬性。
const關鍵字:
在類中宣告常量,不能是define()
函式!必須使用const
關鍵字。與define()
宣告相似,const
關鍵字宣告常量不能帶$
,必須全部大寫!
常量一旦宣告,不能改變。呼叫時與static
一樣,使用類名呼叫Person::常量
。
instanceof操作符:
檢測一個物件,是否是某一個類的例項。(包括爹輩,爺爺輩,太爺爺輩……)
$zhangsan instanceof Person;
【小總結】幾種特殊操作符:
-
.
只能連線字串;"".""
-
=>
宣告陣列時,關聯鍵與值["key"=>"value"]
-
->
物件($this new
出的物件)呼叫成員屬性,成員方法; -
::
使用parent
關鍵字,呼叫父類中的同名方法:parent::say();
,使用類名(和self
)呼叫類中的靜態屬性,靜態方法,以及常量。
魔術方法小總結
-
__construct()
:建構函式,new一個物件時,自動呼叫。 -
__destruct()
:解構函式,當一個物件被銷燬前,自動呼叫。 -
__get()
:訪問類中私有屬性時,自動呼叫。傳遞讀取的屬性名,返回$this->屬性名
-
__set()
:給類的私有屬性賦值時,自動呼叫。傳遞需要設定的屬性名和屬性值; -
__isset()
:使用isset()
檢測物件私有屬性時,自動呼叫。傳遞檢測的屬性名,返回isset($this -> 屬性名);
-
__unset()
:使用unset()
刪除物件私有屬性時,自動呼叫。傳遞刪除的屬性名,方法中執行unset($this -> 屬性名);
-
__toString(
):使用echo
列印物件時,自動呼叫。返回想要在列印物件時,顯示的內容;返回必須是字串; -
__call()
:呼叫一個類中未定義或未公開的方法時,自動呼叫。傳遞被呼叫的函式名,和引數列表陣列; -
__clone()
:當使用clone
關鍵字,克隆一個物件時,自動呼叫。作用是為新克隆的物件進行初始化賦值; -
__sleep()
:物件序列化時,自動呼叫。返回一個陣列,陣列中的值就是可以序列化的屬性; -
__wakeup()
:物件反序列化時,自動呼叫。為反序列化新產生的物件,進行初始化賦值; -
__autoload()
:需要在類外部宣告函式。當例項化一個未宣告的類時,自動呼叫。傳遞例項化的類名,可以使用類名自動載入對應的類檔案。
抽象類和抽象方法
- 什麼是抽象方法?
沒有方法體{}
的方法,必須使用abstract
關鍵字修飾。這樣的方法,我們稱為抽象方法。
abstract function say(); //抽象方法
什麼是抽象類?
使用abstract
關鍵字修飾的類就是抽象類。
abstract class Person{}
抽象類的注意事項:
- 抽象類可以包含非抽象方法;
- 包含抽象方法的類必須是抽象類,抽象類並不一定必須包含抽象方法;
- 抽象類,不能例項化。(抽象類中可能包含抽象方法,抽象方法沒有方法體,例項化呼叫沒有意義)
我們使用抽象類的目的,就是限制例項化!!!
- 子類繼承抽象類,那麼子類必須重寫父類的所有抽象方法,除非,子類也是抽象類。
使用抽象類的作用?
- 限制例項化。(抽象類是一個不完整的類,裡面的抽象方法沒有方法體,所以不能例項化)
- 抽象類為子類的繼承提供一種規範,子類繼承一個抽象類,則必須包含並且實現抽象類中已定的抽象方法。
介面與多型
介面
什麼是介面?
介面是一種規範,提供了一組實現介面的類所必須實現的方法組合。
介面使用interface
關鍵字宣告;
interface Inter{}
- 介面中的所有方法,必須都是抽象方法。
- 介面中的抽象方法不需要也不能使用
abstract
修飾。 - 介面中不能宣告變數,不能有屬性,只能使用常量!!!
介面可以繼承介面,使用extends關鍵字!
介面使用extends繼承介面,可以實現多繼承。
interface int1 extends Inter,Inter2{}
類可以實現介面,使用implements
關鍵字!
類使用implements
實現介面,可同時實現多個介面,多個介面間逗號分隔;
abstract class Person implements Inter,Inter2{}
一個類實現一個或多個介面,那麼這個類,必須實現所有介面中的所有抽象方法!
除非,這個類是抽象類。
介面&&抽象類區別
- 宣告方式上,介面使用
interface
關鍵字,抽象類使用abstract class
。 - 實現/繼承方式上,一個類使用
extends
繼承抽象類,使用implements
實現介面。 - 抽象類只能單繼承,介面可以多實現。(介面
extends
介面)、多實現(類implements
介面) - 抽象類中可以有非抽象方法,介面中只能有抽象方法,不能有費抽象方法。抽象類中的抽象方法必須使用
abstract
關鍵字修飾,介面中抽象方法不能帶修飾詞。 - 抽象類是個類,可以有屬性、變數;介面中只能有常量。
多型
多型
一個類,被多個子類繼承。
如果,這個類的某個方法,在多個子類中,表現出不同的功能,我們稱這種行為為多型。
實現多型的必要途徑:
- 子類繼承父類;
- 子類重寫父類方法;
- 父類引用指向子類物件