摘要:特殊在,方法被對象調用執行時,會自動確定是那個對象調用的該方法,會使用該對象為方法內的賦值構造析構類,沒有作用域,作用域,只是講,函數內和函數外。析構在對象消失對象被銷毀時,也會自動執行一個方法,稱之為析構方法。
相關定義
對象(object):現實生活中的實體,在編程語言中的體現。實體都有屬性和功能。一組數據,和操作管理這些數據的操作,定義在一起就形成了一個實體,稱之為對象。(屬性和方法的集合)
屬性(property),對象中,對象所擁有的數據,稱之為對象的屬性。
方法(method):對象中,對象所擁有的管理數據的能力,稱之為方法。
在php中,對象通過對類的實體化形成的對象。
類(class): 對象的模子。 一類的對象抽取出來。一個將對象的行為和屬性的一個抽象。就是一個定義。規定了對象應該有哪些屬性,應該有哪些操作(方法)。
實例化:一個動作,根據類中所定義的對象的特征,形成一個對象的過程。
注意: php 中,對象一定是通過類的實例化來的。類的地位,只是得到對象的方法。
可以使用php 預定義的類。
stdclass . 通過實例化該類,就可以得到一個對象。
實例化: 通過關鍵字new 完成.
class 關鍵字
class 類名 { 成員 }
在定義一個類的時,需要知道這個類所實例化的對象,應該具有哪些屬性和方法。
增加屬性: 對象所擁有的數據,就是定義在類中變量。
增加方法: 對象所擁有的操作,可執行代碼就是操作.定義在類中的函數.
注意:類中的成員(屬性和方法),需要增加訪問修飾限定符,簡單的增加--public
在聲明成員時,不能直接使用變量,需要使用關鍵字來聲明
通過類得到對象 ,操作符 new 完成.
實例化好后,典型的應該保存在變量內
$stu = new Student();
一個類,可以實例化多個對象,多個不同的對象
對象操作屬性和方法使用操作符 ->
// 對象 -> 成員 $stu->a; // 10
注意,屬性名前$沒有了
如果存在$, 語法就 變成 屬性名(屬性標識符) 由變量充當,變成了可變屬性的語法
$property_name = "stu_id"; echo $stu->$property_name;
屬性名,同變量名一樣,嚴格區分大小寫。
保存數據標識符,就區分大小寫。
如果是結構標識符,就區分大小寫。 比如:函數,類,方法名。
訪問方法:
$stu->study();
方法名,不區分大小寫
方法名,支持可變方法,方法名有變量來代替
類名,可以使用變量來代替。
$class_name = "Student"; $stu = new $class_name;屬性
可以在定義時,為屬性直接設置初始值,但是必須已經存在的數據(類似于函數參數的默認值)
如果沒有默認值,則值為null
屬性,每個對象的同名屬性,都可以具有不同的值, 每個對象所擁有的屬性時不同的數據空間。
因為在實例化對象時,php會為每個對象分配一個獨立的空間,保存對象的屬性
由于,常規的,每個對象,應該擁有不同的屬性值。
建議,在得到該對象時,應該對對象所擁有的屬性,進行初始化。
方法,也是屬于某個對象。但是通常,方法內所操作的數據,都是該對象所擁有的數據。
在使用對象調用某個方法時,會自動地將當前對象傳遞到該方法內(類似于參數的傳遞)。 方法內,會自動使用變量 $this 來接收這個對象。因此,可以在方法內,通過$this方法調用該方法的對象。
如何在方法中,訪問對象,$this
$this,這個對象。調用該方法的對象。
$this 就是一個方法內的局部變量。特殊在,方法被對象調用執行時,PHP會自動確定是那個對象調用的該方法,會使用該對象為方法內的$this賦值
構造&析構類,沒有作用域, 作用域,只是講,函數內和函數外。
注意,想訪問到對象的成員(屬性和方法),一定要先找到對象和方法。
class Student { public $stu_id; public $stu_name; public function sayName() { echo $this->stu_name; // zf var_dump($stu_name); // 報錯 Undefined variable var_dump($GLOBALS["stu_name"]); // 報錯 Undefined variable } } $stu = new Student(); $stu->stu_id = "16.7.3"; $stu->stu_name = "zf"; $stu->sayName();
類中定義的屬性,不是相當于類中定義的方法的全局變量。不能直接在方法中使用屬性變量的形式訪問:
如果直接操作(輸出)這個類名,則不會當做類來看待,而是當做常量來看待。
構造在得到對象時,幾乎都需要,對對象屬性進行初始化,而且都是一樣的操作。在一個操作中完成初始化,然后對該方法進行多次調用。
在實例化類得到對象時被自動地調用
主要承擔的工作是初始化對象屬性
對象的屬性初始化
只要得到了對象,對象的屬性就應該被賦予新的值。
如果某些屬性,在對象出現時,可以被設置為某些特定的值。就可以在聲明類時,為聲明的屬性設置默認值。
stu_id = $id; $this->stu_name = $name; }
調用該方法初始化
stu_id = $id; $this->stu_name = $name; } } $stu1 = new Student; // $stu1->stu_id = 100; // $stu1->stu_name = "李尋歡"; $stu1->init(100,"李尋歡"); var_dump($stu1);
再前進一步:
是否可以在實例化后,自動調用該初始化方法的方法。
PHP的 oop 機制,在new完成時,會試著調用一個叫做 __constructor() 的方法.如果將初始化的代碼,寫到這個方法內,就可以完成自動初始化。
該方法,在通過類實例化對象,也叫構造對象時,被自動調用的,常常用于初始化對象,這個方法被叫做 構造方法。(此方法,就是比普通方法多了一個自動調用的功能)
只要得到了對象,對象的屬性就應該被賦予新的值
由于不用去調用這個構造函數,如何傳參呢?
在實例化時,通過在類名后,增加實例化列表形式,為構造函數方法傳參。stu_id = $id; $this->stu_name = $name; } } $stu1 = new Student(100,"李尋歡");此時,需要注意,實例化時,類名后,可以增加括號,取決于,該對象的構造方法,是否需要參數,如果不需要,則可以省略,或者是一個空括號。如果需要則一定有括號,括號內是實參列表
$stu1 = new Student; $stu2 = new Student(); // 需要參數 $stu2 = new Student("1000","阿飛");構造方法的兼容性問題
PHP5,構造方法的名字, 就是 __construct(); 在php5之前,構造方法名字與類同名。為了兼容,也同時支持這個與類同名的構造方法class Student { public function Student ( $id, $name ) { } }如果同時出現,如何處理
先寫__construct(); 后寫Student;找,__construct.
先寫Stuendt(); 后寫__construct();
找 __construct. 有一個錯誤提示。
常見的寫法:
public function __construct ( $id, $name ) { $this->Student($id,$name); } public function Student ( $id,$name ) { $this->stu_id = $id; $this->stu_name = $name; }構造是這個對象來的時候的方法,對象實例化類的時候,才形成的。
如果某些屬性,在對象出現時,可以被設置為某些特定的值。就可以在聲明類時,為聲明的屬性設置默認值
如果沒有定義__construct()可以不用執行
析構
但是一旦定義了構造方法,那么構造(實例化)的過程,一定要包括這個調用構造方法的過程(構造方法一定會執行)。在對象消失(對象被銷毀時),也會自動執行一個方法,稱之為析構方法。
析構方法名字為 __destruct();
也會自動被調用。完全取決于這個對象是否被銷毀。該方法,用于釋放對象所占用的額外資源。并不是釋放對象本身.(對象本身,是由垃圾回收機制去銷毀) 不是對象本身的內存空間。
public function __destruct () { // 釋放資源 mysql_close(); }什么情況下,對象會被銷毀:
腳本周期結束,自動銷毀,幾個對象銷毀幾次。
銷毀保存該對象的變量時:
unset($stu1);
保存對象的變量,被賦值了其他數據。 任何新值都可以,甚至是原來類的新對象。都會導致原對象被銷毀。常見的使用是null.表示銷毀對象的含意
$stu = null;對象間的copy&clone對象間的復制
支持引用傳遞,不用 & 符號, 因此不能通過 = 賦值的形式,得到一個新的對象。克隆 clone
利用已有的對象,得到相同的新對象。
需要使用關鍵字 clone 完成。
新對象 = clone 舊對象.常見的操作,在克隆對象時,需要對對象的某些特殊屬性進行修改。意味著,在克隆的時,需要做一些特殊的處理。
使用 在克隆時,自動調用的方法, __clone() 來實現。
自動使用克隆出來的對象,來調用這個__clone()方法,意味著,該方法內的$this,表示新對象;
public function __clone () { // $this 指向克隆對象 $this->stu_id = "1000"; } $stu3 = clone $stu1;PHP中得到新對象兩個方法
實例化(通過類構造對象) ,需要使用構造方法對對象進行初始化。
克隆(通過對象克隆新對象) ,得到一個屬性值與克隆的就對象一模一樣的對象,但是,可以通過__clone 方法進行修改。
魔術方法
靜態成員
特定的情況下,會被自動調用
__ 魔術方法場景: 學生類,每個學生是一個類的對象,需要統計,當前已經存在幾個學生?
如何定義這個計數器?
不能直接用屬于對象的屬性,每個對象所獨有的。
應該找一個對象所共有的數據。
構造方法靜態局部變量,也是不行,原因析構時不能使用。public function __construct () { $this->count++; static $count = 0; $count ++; echo $coun; } public function __destruct () { $this->count--; $count--; }應該找一個能夠被對象所共有的數據并且能夠在多個方法內使用的變量。
使用全局變量即可,在方法內,是可以通過$GLOBALS訪問到全局變量。public function __construct () { $GLOBALS["count"]++; } public function __destruct () { $GLOABLS["count"]--; }全局變量不應該屬于任何的對象或者類.$count 與 類 沒有絲毫的邏輯上的聯系.
應該找一個能夠被對象所共有并且能夠在多個方法內使用的變量,還應該與當前的對象類有邏輯的關系的數據?
可以使用類的靜態成員.靜態成員,指的是邏輯上被所有對象所共享,屬于類的成員稱之為類的靜態成員。
靜態屬性和靜態方法
靜態屬性
保存數據的是靜態屬性,執行功能的是靜態方法使用static 關鍵字聲明的屬性。
該靜態屬性,在邏輯上,是定義在類上面的屬性。(與定義對象上的屬性對應).public static $stu_count = 0;保證一個類,對應一個屬性,意味著,該類的所有對象共用這個屬性。
訪問靜態屬性
通過類來訪問,再利用靜態訪問符號(雙冒號::,范圍解析操作符)
類::成員
Student::$stu_count++;:: 的訪問稱之為靜態訪問,相對的箭頭(->)稱為非靜態訪問(對象訪問)
var_dump(Student::$stu_count);在訪問時,如果是在類內訪問:則可以使用self關鍵字,來代替當前類.
self::$stu_count++;注意,$this和self的區別
$this表示的是這個對象。 $this->
self表示類自己。 self::
parsent當前父類 parent::PHP還支持的寫法:
對象也支持靜態訪問,但是需要使用靜態訪問符號。容易造成混淆,不建議經常使用。$stu3::$stu_count;靜態方法靜態方法的邏輯意義,也是定義在類上的方法。同樣,調用形式,也是通過類:: 來訪問
定義:
public static function sayCount () { echo "run"; }訪問:
Student::sayCount();靜態方法和非靜態方法的主要區別,在于是否可以接收一個對象的執行環境
就是是否可以為方法內的$this 是否可以被賦值只有在對象調用方法時,才能將對象的執行環境傳遞到方法內,$this才可以被賦值.
Class Student { public static $stu_count = 0; public static function sayCount () { var_dump($this); //報錯 echo "run"; } } $stu1 = new Student(); Student::sayCount();無論靜態和非靜態方法,都是一個函數,找到他并執行即可。
一個靜態方法,只能處理靜態數據(靜態屬性)public static function sayCount () { echo self::$stu_count; echo "run"; }PHP中特殊的方法訪問問題
靜態方法應該類來調用,非靜態方法,應該對象調用。
但是:
無論靜態還是非靜態,都可以使用類來訪問。
不過如果類靜態調用一個非靜態方法,會報告一個strict standards 語法不標準的錯誤。
對象也可以都調用Class Student { public function fn1 () { echo "fn1"; } public static function fn2 () { echo "fn2"; } } $stu1 = new Student(); Student::fn1(); Student::fn2(); $stu1->fn1(); $stu1->fn2();區別就在于$this上
類常量
只有在使用對象調用非靜態方法時,才可以使用方法內的$this;
靜態方法,無論如何也不能對$this做處理
而非靜態方法,只有確定了對象,才能確定$this的值類中,保存運行運行周期內,不變的數據。就是常量。
定義
const 關鍵字
const 常量名 = 常量值
沒有訪問修飾限定符.const PI = 3.14;訪問
類::常量名
把屬性值,固定值,定義成常量。self::PI;常見,類中 屬性的選項, 多定義成 常量。
類中可以定義的成員一共有
常量,靜態屬性,靜態方法,非靜態屬性,非靜態方法
除了以上5個,類中,不能直接出現其他語句,例如echo的執行性語句,var_dump(), 等等
簡化一點:常量,屬性,方法$this 表示當前對象
永遠表示$this所在類的對象么?
不是,因為$this的值,不取決于$this所在的類,而是$this所在方法在被調用時執行對象(執行環境)。方法的執行環境(context),當前方法是在哪個對象的環境下執行,方法在哪個對象的環境下執行,該方法內的$this 就表示哪個對象。
繼承和重寫 繼承
執行環境向下環境.一個對象擁有或者使用另一個對象的成員信息,稱之為這個對象繼承自另一個對象。
PHP中,通過在類上,使用特殊的操作達到目的。
繼承是對象的概念,只不過語法上面要通過類來實現。通過在在定義類時,利用extends 來指明當前類的對象 , 繼承那個類的對象。
class C { public $p_c = "value C"; } class D extends C { public $p_d = "value D"; } $d = new D(); var_dump($d->p_d); var_dump($d->p_c);繼承,指的是兩個對象之間,那么哪有這兩個對象?
class C { public $p_c = "value C"; } class D extends C { public $p_d = "value D"; } $d = new D(); var_dump($d instanceof D); // true var_dump($d instanceof C); // true相關概念
class D extends C {}
D 類對象 繼承自 C 類對象
父類:被繼承的類,C類
子類:需要繼承的類,D類功能上:
基類:C類是D類的基類
擴展類:D類是C類的擴展類繼承概念體現在對象上,語法體現在類上。
語法意義就是,面向對象語法中的,代碼的重用PHP是單繼承
單繼承,一個類只能繼承自另一個類,不能同時繼承多個類。但是一個類可以被多個類繼承。繼承的目的:
重寫override
在于擴展,或使用某個類已經存在的操作和數據。
繼承,在編程上,可以是理解成OOP代碼的共用。只有發生在繼承上,才會出現,是一個現象。
如果子類,與父類,出現同名的成員(屬性方法),則在實例化子類對象時,只會得到子類中定義的成員,稱之為重寫。成員沖突
繼承時,如果發生成員沖突,PHP的處理方式,為重寫。就是子類同名成員會覆蓋父類的同名成員。不能看到父類的成員。class P { public $name = "p"; public function sayNmae () { echo "parent::name", $this->name; } } class C extends P { public $name = "C"; public function sayName () { echo "self::name",$this->name; } } $obj = new C(); echo $obj->name; // C echo "
"; $obj->sayName(); // self::nameC某些情況下,重寫時一定發生的,例如構造等。
如果需要,強制執行被重寫的父類方法,可以顯式的使用父類來調用相應的父類方法即可。public function __construct () { P::__construct(); }可以使用一個關鍵字,在類內,代替當前類的父類。
parent 關鍵字,代替父類。
一旦重寫,父類代碼就不會在執行了。P::__construct(); parent::__construct();如果說父類的構造需要相應的參數,則需要將再調用時,將父類的構造方法需要的參數,也傳遞到方法內。
class GoodsBook extends Goods { public $page; public function __construct ( $name, $price, $pages ) { parent::__constuct($name, $price); $this->pages = $pages; } }一般2到3層的繼承,就基本上可以
instanceof 操作符
用于判斷一個對象是否是某個類的實例
$this的確定只有在使用對象調用非靜態方法時,才可以使用$this!
哪個對象調用方法,方法內$this就是那個對象。
對象環境,是可以向下傳遞。
如果當前方法內,已經確定了對象環境。在該方法內,如果出現了靜態調用非靜態方法,此時當前的對象環境,會傳遞到靜態方法調用的非靜態方法中。
class A { public function in_a () { var_dump($this); } } class B { public function in_b () { var_dump($this); // B 實例化對象 echo "PHP連接MySql 設置屬性和連接方法
"; A::in_a(); // B 實例化對象 } } $obj = new B(); $obj->in_b();構造方法內,使用數組作為參數,如果參數過多,便于管理。
利用參數,為對象屬性進行賦值
常見的,在實際操作中:為屬性設置默認值。如果用戶在實例化時,傳遞了屬性參數,則使用用戶傳遞的,否則使用默認值。// $params Array public function __construct ( $params ) { $this->host = isset($params["host"]) ? $params["host"] : "127.0.0.1"; $this->port = isset($params["port"]) ? $params["port"] : "3306"; $this->user = isset($params["user"]) ? $params["user"] : "root"; $this->pass = isset($params["pass"]) ? $params["pass"] : ""; $this->charset = isset($params["charset"]) ? $params["charset"] : "utf8"; $this->dbname = isset($params["dbname"]) ? $params["dbname"] : ""; $this->prefix = isset($params["prefix"]) ? $params["prefix"] : ""; }連接應該在構造方法內完成
要求,一個功能,盡量使用一個方法獨立完成,一個大的功能,是由多個小功能組合起來。// 連接數據庫 public function connect () { mysql_connect("$this->host:$this->port",$this->user,$this->pass); }為當前的功能類,增加一個屬性,保存這個鏈接資源
// 運行時生成的信息 public $link;在連接成功后,為屬性賦值
連接失敗,給出提示public function connect () { if ( $link = mysql_connect("$this->host:$this->port",$this->user,$this->pass) ) { $this->link = $link; } else { echo "連接失敗"; exit; } }設置字符集,提取執行SQL設置字符集
// 設置字符集 public function setCharset () { $query = "set names $this->charset"; if ( mysql_query($query) ) { // 成功 } else { // 失敗 echo "字符串設置失敗"; exit; } }執行sql語句
// 執行SQL語句 public function query ( $query ) { if ( $reslut = mysql_query($query,$this->link) ) { // 執行成功 return $reslut; } else { // 執行錯誤 ,給出提示 echo "SQL執行錯誤,錯誤信息:
"; echo "出錯的SQL為:", $query ,"
link), "
"; echo "錯誤的信息為:", mysql_error($this->link), "
"; // 簡單錯誤處理,一旦出錯,直接停止掉腳本執行 die; // 如果在完整的處理過程中, 出錯應該余下代碼繼續執行 // 需要判斷是否執行成功 ,返回false return false; } }
"127.0.0.1","port","3306","user",); public function __construct( $params ){ //對屬性初始化 //如果配置項選項存在,則使用用戶的,否則使用默認的。 $this->host = isset( $params["host"] ) ? $params["host"] : "127.0.0.1" ; $this->port = isset( $params["port"] ) ? $params["port"] : "3306"; $this->user = isset( $params["user"] ) ? $params["user"] : "root"; $this->pass = isset( $params["pass"] ) ? $params["pass"] : ""; $this->charset = isset( $params["charset"] ) ? $params["charset"] : "utf8"; $this->dbname = isset( $params["dbname"] ) ? $params["dbname"] : ""; $this->prefix = isset( $params["prefix"] ) ? $params["prefix"] : ""; //連接 $this->connect(); //設置字符集 $this->setCharset(); //選擇默認數據庫 $this->selectDB(); } // 連接數據庫服務器 public function connect(){ if( $link = mysql_connect("$this->host:$this->port",$this->user,$this->pass) ){ //連接成功 $this->link = $link; } else { //連接失敗 echo "連接失敗 !"; exit; } } // 設置字符集 public function setCharset(){ $query = "set names $this->charset"; $this->query($query); } // 選擇默認數據庫 public function selectDB(){ $query = "use $this->dbname"; $this->query($query); } // 執行SQL語句 // @param $query string 需要執行的SQL語句. // @return mixed 成功返回資源結果集或者true. 失敗返回false. public function query( $query ){ if( $result = mysql_query($query,$this->link) ){ //執行成功 return $result; } else { //執行失敗 //給出錯誤提示 echo "SQL執行出錯,錯誤信息如下:訪問控制說明 訪問修飾限定符
"; echo "出錯的SQL語句為:",$query,"
"; echo "錯誤的代碼為:",mysql_errno($this->link),"
"; echo "錯誤的信息為:",mysql_error($this->link),"
"; //簡單錯誤處理,一旦出錯,直接停止腳本執行. die; //如果再完整的處理過程中,出錯應該余下的代碼繼續執行。 //需要判斷是否執行成功,返回false. // return false; } } } $db = new MYSQLDB( array("host"=>"127.0.0.1","port"=>3306,"user"=>"root","pass"=>"") ); var_dump($db); ?>public , protacted, private
用于描述,一個成員(屬性,方法)在哪里才能被訪問的。php5中,要求所有的成員(屬性和方法)都應該受訪問修飾的控制,聲明時,前面都應該存在訪問修飾限定符
存在例外,為了兼容.
在聲明屬性時,可以使用特殊的 var 關鍵字.相當于使用public來聲明.
class Student { var $stu_id; }方法中,聲明時,可以省略訪問修飾限定符.相當于public.
class Student { function sayCount () {} }public ,公共的,成員在類內,繼承鏈上類內,和類外都可以訪問到(任何地方)
protacted , 保護的,就是類內和繼承鏈上的類內 都可以訪問.
private , 私有的, 類內.PHP是采用類的概念,進行成員的限制訪問的。
PHP將訪問的代碼,分成三大區域:類內,類外,繼承鏈上的類內。
類內: 定義該成員所在類的內部.
繼承鏈上的類內
類外類外訪問:只有公共可以訪問
類內訪問:類中所定義的方法內
所有的限定都可以繼承鏈上的類內:
保護的和公共的可以被訪問,而私有的不可以被訪問.根據:成員在哪里定義 與 成員在哪里訪問 來決定 類內 , 類外 還是 繼承鏈上的類內。
注意
受保護,繼承鏈上的類內
父類內定義,子類內訪問. 反過來,子類內定義父類內可以訪問.私有成員的問題
出現在 私有屬性 的重寫上
PHP會記錄所有的私有屬性,同時會記錄私有屬性所在的類,在不同類內訪問不同的私有屬性時是不同的。而, 公共的和 受保護的, 則只會記錄一次,明顯的被重寫了;
寫繼承的時候,如果是有屬性被重寫了,要注意到,當前所訪問的私有屬性是哪一個私有屬性.私有成員不能被重寫。意味著,在相應的私有屬性定義在類中,才能訪問到相應的私有屬性。
建議是:如果需要通過繼承,就使用保護的,少用私有的。在沒有繼承時,盡量使用私有的。
重寫的問題,要先明確訪問的究竟是哪里所定義的。
在重寫時,如果重寫成員的訪問級別不一致。子類的級別比父類的級別,相等或者弱,可以。強,不行。
封裝怎么使用訪問修飾,如何選擇
原則:盡量提高,類對其成員的控制能力. 能用private,盡量使用private,能用protected就不用public.
一個原則,盡量體現封裝性。封裝性,指的是,盡量隱藏內部實現,而僅僅開發外部操作接口!
語法上,就是,將不需要外部使用的屬性,方法,都私有化(保護化),而僅僅留下一些必要的公共方法!選擇訪問修飾限定符,體現oop思想的封裝性的特點.
封裝性:隱藏對象的內部實現細節,只提供外部操作方法。
對象的外部操作方法,稱之為對象的接口(interface). 接口在語法上,就是一些公共的方法.
封裝:只有操作接口可以被看到,內部實現都被隱藏.經典的:幾乎所有的屬性和大部分的方法都是私有(如果有繼承的話,會有受保護).只有一些供外部調用者使用的方法,是公共的.
類: 繼承和實例化
類: 調用其靜態成員
類: 作為其他類的基礎類,被繼承
兩大功能:實例化對象, 基礎類被繼承抽象類: 只能被繼承,不能被實例化對象
final類
final類: 只能被實例化,不能被繼承該類,只能被實例化對象不能用于被繼承。
設計時,該類不能再擴展,就應該通過語法,final限制,其它用戶擴展該類.
最終,在繼承鏈條上, 最末的一個類,其下不能再出現子類,意味著不能被繼承。定義
在 class 前 加 final關鍵字,即可.final class GoodsBook extends Goods {}如果繼承該類,會報錯
因此,final 類的作用,是在語法上,人為的限定哪些類不能被繼承.
final 關鍵字的另一個用法, 用于限制方法,在所屬類,被繼承時,該方法不能被重寫.
// 所有商品輸出價格方式一致 final public function sayPrice() { echo "¥",$this->shop_price; }抽象類abstract有一種類,只能被繼承,不能被實例化對象。原因就是這個類的定義不完整. (有了一半的藏寶圖,需要找到其它的藏寶圖,補充完整) 為什么會不完整? 因為PHP支持定義一種,只有方法的聲明部分,而沒有方法的實現部分的不完整方法。(類所包含的元素,不完整,可以不完整的是方法)。
如果某個類,包含了這種不完整的方法,就不是一個完整的類,就不能實例化對象!
不完整的類, 稱之為 抽象類. abstract class
所包含的不完整的方法,稱之為:抽象方法. abstract method
定義
包含了抽象方法的類,就是抽象類語法
定義抽象方法,利用一個叫 abstruct 的關鍵字,告知PHP某個方法時一個抽象方法,沒有方法體abstract public funciton sayName(); // 要注意分號定義一個抽象類
如果一個類包含了抽象方法,也就是抽象類,因此也需要使用 abstruct 關鍵字聲明.沒有實例化的能力
實例化報錯
只有被繼承的能力
如果繼承某個抽象類的類是非抽象類的話,就一定要將不完整的抽象方法實現。否則該類也應該是一個抽象類.抽象類的具體功能
1:限制子類的結構由于抽象類,只能被繼承,而且如果其子類不是一個抽象類的話,要求必須實現抽象類所定義的抽象方法,因此抽象類的功能也可以理解成,用于限制其子類(擴展類)的結構.
就可以保證,同一系列的處理類,所擁有的結構是一致的。將來可以實現無縫對接,任意切換(熱插拔) 。
2:可以限制
方法名,參數個數.
訪問修飾控制.
擴展類(子類)的權限限制,弱于抽象類抽象類功能
接口 interface
在可以為子類(擴展類)提供共用操作的同時,限制子類(擴展類)所擁有的方法的結構.
犧牲掉了實例化對象的功能.一個對象的封裝,分為兩大部分
內部實現
就是操作接口
PHP中,可以規定,一個對象應該具有哪些公共的外部操作,使用interface來規定
公共的方法就是接口
用于規定一個對象應該擁有哪些公共的操作方法(接口),這個東西也叫接口(公共操作方法的集合).
接口(interface 結構,公共方法的集合)公共方法(接口方法)
定義:用于限定某個對象所必須擁有的公共操作方法的一種結構,稱之為接口(interface).
就是用于限制公共方法。語法:
定義這個接口結構,使用interface 關鍵字.接口定義的都是公共的方法interface 接口名 { 公共操作方法列表 } interfece I_Goods { public function sayName(); public function sayPrice(); }注意:
接口方法,必須都是公共的
接口內只能有公共方法,不能存在成員變量屬性interfece I_Goods { public $goods_name; //報錯 public function sayName(); public function sayPrice(); }接口內,只能含有未被實現的方法,也叫抽象方法,但是不用abstract關鍵字().
如果是一個完整方法,會報錯。使用接口
限制對象的必要的公共操作
類實現接口 (實現--把你想做的事完成). 利用關鍵字, implements;class Goods implements I_Goods {}這樣,實現該接口的類,必須實現接口內所有的抽象方法。而且可以肯定,該方法一定是公共的外部操作方法
// 定義接口 interface I_Goods { public function sayName(); public function sayPrice(); } // 實現接口 class Goods implements I_Goods { public $goods_name; public $shop_price; public function __construct ( $name, $price ) { $this->goods_name = $name; $this->shop_price = $price; } public function sayName () { echo $this->goods_name; } public function sayPrice () { } } $goods = new Goods("php", 234); $goods->sayName(); $goods->sayPrice();類似于抽象類,比較與接口的區別:
抽象類與普通類之間是繼承關系
普通類繼承抽象了,第一可以得到抽象類中的已有的常規成員,第二才需要實現抽象方法(也不一定是public)接口與普通類之間是實現關系
普通類實現了接口,只能將其沒有實現的公共方法實現接口只用于定義 公共的方法。 而抽象類,什么都可以有。
多實現
上面的功能,理論上講,可以通過抽象類完成。 但是抽象類,不專業
接口專業體現在實現上,因為PHP支持多實現,而僅支持單繼承.class Goods implements I_Goods, I_Shop { }繼承和實現區別
繼承:體現的是將別的對象已經存在的東西,拿來自己用
實現:將某個地方的規定,自己來完成接口之間,也可以繼承
interface I_Shop extend I_Goods { public function saySafe(); }抽象類 pk 接口
繼承 pk 實現PHP對接口的支持,可以定義常量
interface I_Shop extend I_Goods { const PAI = 3.14; }通過類去訪問
class Goods implements I_Shop {}
echo Goods::PAI;辨別
繼承應用 表操作模型
接口是不是類 ?
接口不是類,接口就是獨立的一種結構,適用于限制類的結構。但是可以理解成"接口類似于一個所有的方法都是公共的抽象方法,而且沒有其它成員的抽象類",并不是一個概念。
class_exists(); 判斷一個類是否存在?項目中操作數據庫的典型模式: 數據操作模型(model).
常見的操作
PHP項目中,由于不同的數據實體對應的業務邏輯是不同的,通常的操作數據的方法:為每張表創建一個操作對象class BaseTable {} class GoodsTable extends BaseTable {} class CrtegoryTable extends BaseTable {}如果兩個類有具體的關系,使用繼承.
聚合如果是多個類需要使用另一個類,所提供的功能,還有一種方式,稱之為聚合.
class MySQLDB { public function query () { } } class BrandTable { public $db; public function __construct () { $this->db = new MysqlDB(); $this->db->query(); } } $brand = new BarndTable(); $brand->db;如果,BrandTbale 類 對象,需要使用MySQLDB類對象的相應功能,可以BrandTable對象內,增加一個屬性保存MySQLDB類對象。打到 BarndTable使用MySQLDB 對象的功能目的
這種方式,稱之為 對象的聚合。既然聚合 和 繼承 都可以達到 共用一個類功能的目的,使用的標準
如果說兩個類之間存在邏輯關系上的必然聯系,就建議使用繼承。
反之,如果是邏輯上,一個對象使用另一個對象,此時應該使用對象間的聚合。優劣性
自動加載機制
使用繼承,可以增加類之間的聯系,增加關聯性。
而使用聚合,可以降低類之間的關聯性,可以降低耦合性!保存類的方法:
將類的定義代碼,放置在一個獨立的文件中. 如果需要該類,則直接調用.為了識別,某個類定義文件,常見的,類定義文件的命名方式為:
類名.class.php使用時引入:
require("./MySQLDB.php");思考:
如果項目中,多處使用OOP,導致整個項目中,出現多個 xxx.class.php 文件,每當需要都需要引入 ?使用自動加載,按需引入類文件
需要解決的問題:
1,什么時候需要類?
實例化時,靜態調用時,一個類被繼承時如果使用其子類時。
2,在需要時,應該主動require 系列的代碼, 才可能加載成功,如何做到需要自動執行某段代碼?
3,需要的是類,而載入的是文件,需要解決如何通過 類 來確定 類文件的所在文件.PHP解決了前2個問題,需要解決第三個問題。
PHP提供了一個機制(功能),可以在需要某個類,但是沒有載入這個定義類時.會自動調用某個函數。并同時將當前所需要的類名,作為參數傳遞到這個自動調用的函數內。
PHP提供一個時機,完成自動加載。
這個函數默認不存在,需要用戶自己定義。 函數名為 __autoload();function __autolaod ( $class_name ) { var_dump($class_name); require $class_name.".class.php"; }一旦函數存在,在特定情況(需要類但是沒有找到),則PHP內部會自動調用__autoload().執行在函數體定義的函數,從而完成加載類所在的文件。
分工:
用戶腳本:定義函數,接收需要的類名作為參數,函數需要實現通過類名找到類所在的文件,將其載入即可!
PHP核心(Zend Engine):判斷是否需要類,并且判斷該類是否已經載入了,如果需要但是沒有載入,則嘗試調用用戶定義的__autoload()函數,并將需要的類名傳遞即可!__autoload 函數名得到是類名
而載入的是類文件定義的文件。
因此,自動加載機制的前提:必須要能夠通過類名,推導出,類所在的文件才可以。(定義類文件時,要有規律)注意:
對象序列化
PHP提供了加載代碼的執行時機。用戶腳本,需要完成加載代碼,其中核心任務是,通過所得到的類名,找到類定義所在的文件。序列化,反序列化(串行化,反串行化)
數據持久化
在數據保存,面臨一個問題:數據格式。PHP的數據有八種類型之多,文件只能存儲字符串。
一旦數據類型不是字符串類型。就會導致數據不能原樣保存,不能取得原始數據。
如何解決
任何形式的數據,都可以存儲到文件中,并且,在取出來時,原樣得到數據。
在保存,與讀取時,對數據進行轉換與反轉換。序列化
serialize();
原始數據轉成能夠記錄的原始數據信息的字符串反序列化
unserialize();
通過序列化的字符串結果,將原始數據還原!只有在涉及到,數據需要被存儲,或者 傳輸時, 需要對數據進行序列化。
注意的數據類型都可以被序列化與反序列外。資源是例外!
對象的序列化與反序列化
在對象被反序列化時,需要找到當前對象所屬的類才可以被完美的反序列化,如果不能找到屬于的類,那么會變成php內置類:__PHP_Incompleta_Class(不完整類)的一個對象.
因此,只要在反序列話之前,將類載入即可。反序列化,也會觸發自動加載機制。
在序列化時,可以自定義需要序列化的屬性名單!
通過對象的特殊的方法 __sleep();
該方法會在對象被序列化時,自動調用,不需要參數,返回一個數組,每個元素,表示一個屬性名,數組內存在的屬性名,就會被序列化。反之則不會被序列化。// 在序列化時被調用 // 用于負責指明那些屬性需要被序列化 // @return array public function __sleep () { return array("host", "port", "user", "pass", "charset", "dbname"); }在反序列化時,可以自動再次執行某些代碼,從而完成某些資源的初始化!
通過 對象方法:__wakeup()方法
會在 對象被反序列過程中自動調用,所負責的功能,執行反序列話(醒來之后)的初始化工作!// 在反序列化時調用 // 用于 對對象的屬性進行初始化 public function __wakeup () { // 連接數據庫 $this->connect(); // 設置字符集 $this->setCharset(); // 設置默認數據庫 $this->selectDB(); }PHP 自動調用, 用戶腳本只需要定義。在特定的功能調用特定的方法。稱之為:魔術方法。
單例模式單例,單個實例。設計模式的一種,通過一種語法的設計,保證類(對象)上具有某些特征,這樣的一種語法實現,就可以稱之為設計模式。
其中,設計可以保證一個類,有且只有一個對象,這種設計模式就稱之為單例模式。單例模式的作用,單例的設計,是適用于使用一個對象可以完成所有業務邏輯的類
典型的實現,在類上,做限制:三私一公。
某個類只要實例化一次就可以完成所有的業務邏輯!
但是實例化多次也可以。顯然,應該保證對象被實例化一次,從而節約資源空間!
MySQLDB類,如何設計,從而達到保證該類 有且只有一個對象.
原因:使用者,可以無限次的去實例化 這個類。三私一公
1:限制,用戶無限制執行new
在實例化的過程中,需要執行構造方法,如果執行不成功,那么實例化過程失敗。因此,將構造方法,私有化(保護化),可以保證在類外進行實例化的工作都會失敗。class MySQLDB { private function __construct () { } }2,類,不能實例化了,不能再類外實例化,可以在類內實例化,類內執行new操作。
因此:在類中,增加一個公共的靜態的方法,在方法內,執行new實例化類對象。
class MySQLDB { // 私有化構造函數 private function __construct () { } // 執行new public static function getInstance () { return new MySQLDB(); } } $db1 = MySQLDB::getInstance(); $db2 = MySQLDB::getInstance(); var_dump($db1, $db2);3,可以無限制的調用getInstance得到任意多個對象!但是此時得到對象都需要經過 getInstance,可以在getInstance內,增加限制,使用戶得到對象的改變!
在實例化好對象后,將對象存起來,下次再執行時,判斷是否已經有保存的,有了,直接返回已經存在的,沒有,實例化后,保存起來,再返回!
增加一個私有的靜態屬性,在 getinstance內做判斷class MySQLDB { // 判斷是否實例化過 public static $getinstance; // 私有化構造函數 private function __construct () { } // 執行new public static function getInstance () { // 沒有實例化 if ( !(self::$getinstance instanceof self) ) { self::$getinstance = new self; } return self::$getinstance; } }4:還可以通過克隆來得到新對象
需要私有化__clone()方法// 私有化 __clone 方法 private function __clone () { }注意:單例模式傳參問題
在項目的設計層面解決單例的問題
增加一個實例化的方法(函數)
作用:用戶如果想要得到單例對象,就通過調用該函數完成!function getInstace ( $class_name ) { static $objects = array(); if ( !isset($objects[$class_name]) ) { $objects[$class_name] = new $class_name; } return $objects[$class_name]; }優點:靈活,可以針對多個類同時實現單例,而且,類還可以回歸到非單例的效果。
重載overload屬性重載 __set() __get()
傳統意義的重載,一個方法的多種狀態,通常使用,參數類型或者參數個數進行區分,決定當前使用那個函數。
PHP不支持同名方法。
PHP的重載:指的是對不可訪問的成員的操作。
不可訪問的成員: 指的是 不存在 , 或者是,由于訪問修飾控制訪問不到的。// PHP允許為對象增加類中沒有定義的屬性 class Student { public $stu_name; private $stu_id; } $stu = new Student(); var_dump($stu); echo "
"; $stu->stu_gender = "male"; var_dump($stu);
// PHP對屬性除了增加,還支持刪除 class Student { public $stu_name; private $stu_id; } $stu = new Student(); $stu->stu_gender = "male"; unset($stu->stu_name); var_dump($stu);對類后續進行操作,用戶可以操作對象任意的修改對象的結構。
PHP如何處理上面的情況,稱之為重載(屬性的重新加載,成員的重新加載).重載:對成員的重新加載
成員分為:屬性和方法,因此,php支持屬性重載 和 方法重載.
屬性重載PHP對,不可訪問的屬性進行操作處理方法,稱之為屬性重載
PHP需要通過支持 魔術方法完成。
PHP在處理重載屬性時,支持使用4個魔術方法,完成處理屬性重載的情況.__set()
當為不可訪問的屬性賦值時,會被自動調用
會得到兩個參數,當前操作的屬性名,和屬性值!// $p_name string 屬性名 // $p_value mixed 屬性值 public function __set ( $p_name, $p_value ) { // 實現, 處理情況 return ; }典型的,__set()的作用, 用于嚴格控制對象結構,和批量處理可以被修改的屬性。
// $p_name string 屬性名 // $p_value mixed 屬性值 public function __set ( $p_name, $p_value ) { // 實現, 處理情況 // 不可增加任何屬性, 但是可以對 age 和 gender 做出修改 $allow_preperties = array("s_age", "s_gender"); if ( in_array($p_name, $allow_preperties) ) { $this->$p_name = $p_value; // 利用可變屬性,屬性名由變量代替 } } $s = new Student(); $s->s_name = "天機老人"; $s->s_age = 87;__get()
當訪問不可訪問的屬性時,會被自動調用
需要的參數是:一個,為當前操作的屬性名public function __get ( $p_name ) { // 讀取名字 性別 if ( $p_name == "s_name" || $p_name == "s_gender" ) { return $this->$p_name; // 可變屬性 } else { // 讀取年齡 if ( $this->s_gender == "male" ) { return $this->s_age; } } }__unset()
在刪除一個不可訪問的屬性時,可以自動被調用!
需要一個參數 當前操作的屬性名。
此時就可以利用業務邏輯完成 屬性的刪除處理!public function __unset ( $p_name ) { if ( $cound ) { unset($this->$p_name); } }__isset()
在判斷一個不可訪問的屬性是否存在時,被自動調用
需要一個參數,屬性名
注意,次函數需要返回true,或者false,表示屬性是否存在public function __isset ( $p_name ) { if ( $p_name == "s_gender" ) { return true; } else { return false; } }方法重載__call()
當訪問一個不可訪問的對象方法時,會觸發__call()的魔術方法!
需要的參數時:
2個參數,第一是當前的方法名,第二是調用時使用的實參列表!public function _call ( $m_name, $m_args ) { var_dump($m_name, $m_args); } $s = new Student(); $s->sayName("zf", "php");典型應用
1,給出友好提示
2,執行默認操作public function _call ( $m_name, $m_args ) { var_dump($m_name, $m_args); echo "執行了默認操作"; $this->default_action(); } public function default_action () { echo "這里是操作"; } $s = new Student(); $s->sayName("zf", "php");static __callStatic()
與 __call類似,當靜態調用一個不可訪問的方法時,會自動執行!
public static function __callStatic ( $m_name, $m_args ) { var_dump( $m_name, $m_args ); } Student::sayCount();魔術方法
magic method在特定的情況下,會被自動調用的方法,通常負責完成某塊獨立的功能的方法稱之為魔術方法!
特點:1,需要用戶腳本定義,不定義不執行!2,命名方式都是以__開頭__invoke()
將一個對象,當作函數調用時,會觸發該對象的__invoke()方法,由此方法,就可以調用,沒有該方法就不能調用!
__invoke是PHP實現匿名函數 不可或缺的部分!
也可以傳遞參數,為對象傳遞參數,就是為invoke魔術方法傳遞參數!
Closure 類的對象(匿名函數,閉包函數) 可以當作函數調用,為什么我們自己定義的對象不可以當做函數調用? 內置的Closure 類中有 __invoke();方法.
在將一個對象當做函數調用時, 會觸發該對象的 __invoke 方法, 如果方法不存在,對象不能當做函數調用。反之 如果需要調用這個對象, 則可以為該對象增加 __invoke 方法使之執行.
調用對象,其實就是調用該方法,可以接受參數!普通方法一樣處理即可.
魔術方法,只會負責某個特定的功能。當某個特別的情況發生時,才會調用相應的魔術方法。
魔術方法可以沒有, 一旦有的話就會被調用.__toString()
轉換到字符串的意思!
當將對象當作字符串使用時,會自動調用該對象的魔術方法!存在toString 魔術方法,即可以完成轉換。
靜態延遲綁定
toString的返回值,就是轉換的結果,一般轉換對象的標志性的屬性即可!$this永遠代表所在類的對象?
不是
$this的值取決于$this所在方法的執行對象環境.self用于代表所在類么?
是,永遠代表所在類的對象!
self 永遠表示所在的類.
在那個類中被定義,就應該表示那個類.
因為self,parent關鍵字,類的編譯階段就確定了所代表的類.class P { public static $where = "P static,
"; public static function sayWhere () { echo self::$where; } } class C extends P { public static $where = "C static"; } echo P::sayWhere(); // P static, echo C::sayWhere(); // P static,此時,應該是表示當前類的關鍵字,最好應該在調用時決定最好!(self不能做到)
此時,采用一個新的關鍵字,代表當前類,與self不同,在于是 運行時調用時決定,而不是在類編譯時就確定好了的!
使用關鍵字:staticclass P { public static $where = "P static,
"; public static function sayWhere () { echo self::$where; } public static function sayW () { echo static::$where; } } class C extends P { public static $where = "C static,"; } echo P::sayWhere(); // P static, echo C::sayWhere(); // P static, echo "
"; echo P::sayW(); // P static, echo C::sayW(); // C static,static關鍵字的功能
聲明靜態局部變量
聲明靜態成員
當前類,運行的當前類類中,可以表示類的關鍵字:
反射機制
self, 所在類
static,調用類
parent, 父類常見的類和對象操作函數:
instanceof,判斷對象是否是某個類的實例。 get_class(); // 返回對象的類名 class_exists(); // 判斷一個類是否存在 get_declared_classer() // 獲得當前已經存在的類. 包括預定義和用戶自定義的。 method_exists(); property_exists();相應的魔術常量
__CLASS__ , 當前類名
__METHOD__, 當前方法名get_class_methods(); // 得到類的方法
get_class_vars(); // 得到類的屬性
可以得到類內的方法和屬性。
但是,得到的信息較少,只有名字,而且受到訪問修飾符的限制。
如何才能夠詳細精確到類中的結構?
使用到反射機制PHP提供的,用于獲得某個類,或者某種結構(類,函數,方法,屬性,參數,擴展....);
需要使用反射機制,反射機制時利用OOP的語法實現了。
反射機制提供了很多類,針對不同的結構,會使用到不同的反射類對象。
reflection: 反射類. 可以通過對象反過來獲得對象的相關信息.如果需要知道某個類的結構,就應該使用refletionClass
存在一個靜態方法,refletionClass::export() 可以導出一個類的結構報告反射機制可以獲得目標結構的內部結構。
并同時可以驅動目標結構運行起來.(代理執行);name, "類型約束
"; } public function tan($c1, $c2) { echo "
" . $c1 . " ". $c2; } } $p = new Persion(); // $p->say(); // 利用反射對象調用方法 $method = new ReflectionMethod("Persion", "say"); // 反射方法對象 $method->invoke($p); $method2 = new ReflectionMethod("Persion", "tan"); //參數的反射方法 $method2->invokeArgs($p, array("pink", "red")); ?>約束函數,或者方法類參數的類型,只能是某個類的對象。
PHP是弱類型,變量可以存儲任意類型的數據
函數,方法的參數也是可以接受任意類型但是參數,可以被規定為,某個類的固定對象,在參數前增加類名即可。
function sayName ( Student $o ) { echo $o->stu_name; } sayName("php");支持類名,和數組。其它數據類型不支持。
function sayName ( Array $arr ) { }對象的遍歷對象是一個集合數據類型
foreach遍歷
遍歷對象,是依次獲得對象擁有的屬性的信息(注意,訪問修飾符的限制)class Student { public $stu_name; public $stu_age; public $stu_gender; } $obj = new Student(); $obj->stu_name = "李尋歡"; $obj->stu_age = 30; $obj->stu_gender = "male"; foreach ( $obj as $val => $key ) { var_dump($val,$key); echo "
"; }自定義遍歷,iterator
iterator迭代器接口Iterator接口,PHP預定義完成。
實現Iterator接口// 類實現Iterator 接口 class Team implements Iterator { public function rewind () { reset($this->stu_infos); } public function valid () { return key($this->stu_infos) !== null; } public function current () { return current($this->stu_infos); } public function key () { return key($this->stu_infos); } public function next () { next($this->stu_infos); } }魔術常量
__CLASS__:當前類名。注意:可以new self 不可以 new __CLASS__
__METHOD__,當前方法名。區別__FUNCTION__
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/21756.html
摘要:很多情況下,通常一個人類,即創建了一個具體的對象。對象就是數據,對象本身不包含方法。類是相似對象的描述,稱為類的定義,是該類對象的藍圖或原型。在中,對象通過對類的實體化形成的對象。一類的對象抽取出來。注意中,對象一定是通過類的實例化來的。 showImg(https://segmentfault.com/img/bVTJ3H?w=900&h=385); 馬上就要到七夕了,離年底老媽老爸...
摘要:很多情況下,通常一個人類,即創建了一個具體的對象。對象就是數據,對象本身不包含方法。類是相似對象的描述,稱為類的定義,是該類對象的藍圖或原型。在中,對象通過對類的實體化形成的對象。一類的對象抽取出來。注意中,對象一定是通過類的實例化來的。 showImg(https://segmentfault.com/img/bVTJ3H?w=900&h=385); 馬上就要到七夕了,離年底老媽老爸...
摘要:很多情況下,通常一個人類,即創建了一個具體的對象。對象就是數據,對象本身不包含方法。類是相似對象的描述,稱為類的定義,是該類對象的藍圖或原型。在中,對象通過對類的實體化形成的對象。一類的對象抽取出來。注意中,對象一定是通過類的實例化來的。 showImg(https://segmentfault.com/img/bVTJ3H?w=900&h=385); 馬上就要到七夕了,離年底老媽老爸...
閱讀 3457·2021-11-25 09:43
閱讀 2605·2021-09-22 15:54
閱讀 590·2019-08-30 15:55
閱讀 974·2019-08-30 15:55
閱讀 1998·2019-08-30 15:55
閱讀 1741·2019-08-30 15:53
閱讀 3465·2019-08-30 15:52
閱讀 2039·2019-08-30 12:55