單例模式
避免多人開發過程,實例化過多導致資源浪費, 限制只實例化一次的模式
構造方法不能執行則實例化失敗
思路 :
1、 保護或私有構造函數,房子外部實例化
2、 內部開發一個公共的靜態方法,負責實例化
3、 類有一個靜態屬性存放對象
<?php class Single { //設置私有,保存實例狀態 static protected $ins = NULL; //設置為私有,限制類外實例化,若沒有子類可去掉final final protected function __construct() { echo '實例化成功!'; } //設置為靜態方法,類外能調用,實例化 static public function getinstance() { //self代表當前類,判斷是否實例化 if (self::$ins instanceof self) { return self::$ins; } self::$ins = new self(); return self::$ins; } }
$s1 = Single::getinstance(); $s2 = Single::getinstance();
//子類繼承父類若還要單例,要用final修飾父類構造方法, //阻止子類重寫構造方法自己去new的問題 class Single2 extends Single { }
$s11 = Single2::getInstance(); $s12 = Single2::getInstance();
if ($s11 === $s12) { echo "相等"; } ?>
final
final 不能修飾屬性
final 修飾方法,此方法能繼承,不能被重寫
final 修飾類,則此類 不能夠被繼承
魔術方法
是指某些情況下,會自動調用的方法,稱為魔術方法 ; 感覺一般都是那些權限不允許調用或者是不存在的屬性 才會觸發魔術方法
PHP面向對象中,提供了這幾個魔術方法,
他們的特點 都是以雙下划線__開頭的
__construct(), __destruct(), __call(), __callStatic(), __get(), __set(), __isset(), __unset(), __sleep(), __wakeup(), __toString(), __invoke(), __set_state() 和 __clone()
__clone() :克隆方法,當對象被克隆時,將會自動調用
__get() :當我們調用一個權限上不允許調用的屬性,和不存在的屬性時,__get魔術方法會自動調用,並且自動傳參,參數值是屬性名。醬紫就能避免系統會直接報錯,甚至fatal error,通過__get()我們就能自定義用戶訪問時的處理行為。注意如果是數組形式的話那么這里的$p相當於鍵值(即是下標)
流程:
$lily->age--無權-->__get(age);
$lily->friend--沒有此屬性-->__get('friend');
__set() :當為無權操作的屬性賦值時,或不存在的屬性賦值時,__set()自動調用,且自動傳2個參數 屬性 屬性值
__isset() :當用isset()判斷對象不可見的屬性時(protected/private/不存在的屬性)
會引發 __isset()來執行
isset($obj->xyz) 屬性為真,能說明 類聲明了一個xyz屬性嗎?
答:不能,因為isset($hua->tail)----沒有tail屬性---->__isset('tail');-à 如果__isset返回1則不能
__unset() : 當 用unset 銷毀對象的不可見屬性時,會引發 __unset();
__call($a,$b) : 調用不可見(不存在或無權限)的方法時,自動調用;前參數是方法名,后是傳參
__callStatic($a,$b) : 是調用不可見的靜態方法時,自動調用.
__call是調用不可見(不存在或無權限)的方法時,自動調用
$lisi->say(1,2,3);-----沒有say()方法----> __call('say',array(1,2,3))運行
__autoload() : 如果調用某個不存在的類,在報錯之前,系統會調用__autoload($n)函數,並把"類名"自動傳給__autoload函數我們自然可以在__autoload里 加載需要的類!
魔術方法的應用
TP中設置用戶注冊的做法: 把表單中接受到的信息,直接付給一個對象的屬性,然后
對象 add() 用sql語句寫到數據庫中
具體的思路
在一個類中設置一個空數組,利用__set()把傳進來的值放到數組中,然后利用 add方法 與數據庫交互 ; 醬紫避免了與其他同名屬性的沖突
都是使用魔法方法來進行操作
$date = array() 設置一個空數組
implode() :將數組按值進行分割
array_keys(array,value) :獲得數組中鍵名並以數組的形式 ; 前者數組名必選 , 后者數組值可選 ; 只選前者則返回所有鍵名 ; 兩者選返回對應值得鍵名
2015/7/23
重載與重寫
重寫/覆蓋 override:子類重寫了父類的同名方法。只要子類有該方法只調用子類的無論參數是否一致
<?php //模仿重載的功能 class Circle { public function area() { $a = func_get_args(); $num = count($a); if ($num == 0) { echo '傳入參數' . "\n"; } else if ($num == 1) { echo 3.14 * $a[0] . "\n"; //學會利用已有的條件進行分析 } else if ($num == 2) { echo $a[0] * $a[1] . "\n"; } } }
$c = new Circle(); $c->area(); $c->area(3);
class P { public function a() { echo "nihao"; } }
$b = new P(); $b->a(2123, 'sdf'); ?>
重載 overload: 指存在多個同名方法,但參數類型/個數不同,傳不同的參數,調用不同的方法。但是在PHP中,不允許存在多個同名方法.因此,不能夠完成java,c++中的這種重載
但是,PHP的靈活,能達到類似的效果
<?php function __autoload($n){ require('./' . $n . '.php'); echo "加載成功!"; } $test = new autoload_class(); $test->say(); ?>
常量 (常量名全大寫,不帶$)
普通常量 :define('常量名',常量值); 全局有效.無論是頁面內,函數內,類內,都可以訪問 ;
類內常量 : 類常量 在類內用 const 聲明即可;前面不用加修飾符,;且權限是public的,即外部也可以訪問
作用域在類內,類似於靜態屬性 ;又是常量,則不可改.
其實就是"不可改變的靜態屬性",即是能夠 類名::常量名 訪問
魔術常量
1:無法手動修改他的值,所以叫常量
2:但是值又是隨環境變動的,所以叫魔術
__FILE__ 返回當前文件的路徑+文件名
在框架開發或者是網站初始化腳本中,用來計算網站的根目錄
__DIR__ 返回當前的文件路徑
__LINE__ 返回當前的行號
在框架中,可以用來在debug時,記錄錯誤信息
__CLASS__ 返回當前的類名
__METHOD__ 返回當前的方法名
延遲綁定
注意 :static::方法名 表示調用 對象類中的方法 或者屬性
注意 一般書寫的時候public等權限修飾符在static前,增加規范可讀性
抽象
抽象類 :類前加 abstract 是抽象類,是抽象方法抽象類不能 new 來實例化,有抽象方法,則此類必是抽象類;抽象類,內未必有抽象方法
抽象方法 :方法前加 abstract,抽象方法 不能有方法體,所以沒有{} ; 而是直接();
子類 繼承 抽象的父類 必須 重寫父類的抽象方法!並且參數個數要一樣
//利用面向對象思想實現不同語言首頁歡迎!
//抽象類就是個模板,你們子類繼承我的類和方法自己搞自己想要弄的東西
//比如我想要開發英語語言,只要增加一個子類,不用修改父類的東西
//所以面向對象是可插拔的
<?php //利用面向對象思想實現不同語言首頁歡迎! //抽象類就是個模板,你們子類繼承我的類和方法自己搞自己想要弄的東西 //比如我想要開發英語語言,只要增加一個子類,不用修改父類的東西 //所以面向對象是可插拔的 abstract class Language { //抽象方法 public abstract function wel(); } class China extends Language { public function wel() { echo "歡迎!"; } } class English extends Language { public function wel($a) { echo "Welcome!"; } } $language = 'China'; $w = new $language(); //666 $w->wel(); ?>
說明 : 抽象類就是一個模板,我不用擔心同類的類不會做,反正你只要根據我的模板做就不會錯。從而達到兼容多種不同的情況 和 避免代碼大量的修改 和 代碼的規范(方法一致)
比如 :
公司網站要上線了,要選擇什么數據庫? 先弄個mysql開發着,到時候有問題再換也行。那么換數據庫,會不會以前的代碼又得重寫? 其實不用擔心,用抽象類!開發者,開發時,就以db抽象類來開發。不管上線時,真正用什么數據庫,我只需要再寫一份如下類(右圖)即可。所以業務邏輯層不用改,因為都實現的db抽象類。
接口
(沒有括號) interface 接口名 { }
類如果是一種事物/動物的抽象,那么 接口則是事物/動物的功能的抽象,即再把它們的功能各拆成小塊自由組合成新的物種。
以人類為例, class Human 是人的草圖而接口 是零件可以用多種零件組合出一種新特種來.
1、接口本身即是抽象的,內部聲明的方法 默認也是抽象的.不用加 abstract
2、一個類可以一次性實現多個接口.語法用 implements 實現 (把我這幾個功能實現了)
3、接口可繼承另一個接口, 用extends ;注意實現時須把繼承的接口的方
4、接口是一堆抽象方法的說明,不能加屬性
5、接口就是供組裝成類用的,封閉起來沒有意義,因此方法只能是public
<?php /* 接口 就更加抽象了,比如一個社交網站,關於用戶的處理是核心應用。 登陸 退出 寫信 看信 招呼 更換心情 吃飯 罵人 搗亂 示愛 撩騷 這么多的方法,都是用戶的方法,可寫一個user類,全包裝起來 但是,分析用戶一次性使不了這么方法。於是分開多個類 用戶信息類:{登陸,寫信,看信,招呼,更換心情,退出} 用戶娛樂類:{登陸,罵人,搗亂,示愛,撩騷,退出} */ interface UserBase { //注意沒括號 public function login($u, $p); public function logout(); //注意是抽象方法 } interface UserMsg { public function wirteMsg($to, $title, $content); public function readMsg($from, $title); } interface UserFun { public function spit($to); public function showLove($to); } /* 作為調用者, 我不需要了解你的用戶信息類,用戶娛樂類, 我就可以知道如何調用這兩個類 因為: 這兩個類 都要實現 上述接口. 通過這個接口,就可以規范開發. */ class User implements UserBase { public function login($u, $p) { echo "用戶登錄"; } public function logout() { echo "用戶注銷"; } } ?>
包含類進來
include/require能夠包含某個php文件,但是不知道是否調用過;改進使用魔術方法__aotoload($)
__autoload() : 如果調用某個不存在的類,在報錯之前,系統會調用__autoload($n)函數,並把"類名"自動傳給__autoload函數我們自然可以在__autoload里 加載需要的類!
★自定義 自動加載方法
通知系統,讓系統知道--我自己寫了一個自動加載方法,用這個spl_auto_register($n)
★方法中能定義一個類,但是想要執行類里面的東西,必須先動態調用方法(直接方法名),有return也不關事
異常
討論:如何判斷mysql類連接是否成功? 利用 在方法里面return?
實例化后返回一個對象,無法確定是否連接成功 。 return無法干擾,仍然是返回對象
解決這種問題,用異常類 Exception
注意 @mysql_connect(); //@表示忽略此處輸出的錯誤
關閉所有的錯誤報告 : Error_reporting(0);
{ protected $conn = NULL; public function __construct() { $this->conn = mysql_connect('localhost','root','111'); if (!$this->conn) //如果連接失敗了,拋出錯誤 { $e = new Exception('失敗了!',9); // throw $e; //拋出異常 } } } //若拋出異常,沒有接受處理則會報錯 try // 可能出現錯誤的代碼並嘗試捕捉錯誤信息 { $my = new mysql(); }catch(Exception $e) // 注意括號,處理錯誤 { echo $e->getMessage(); echo '錯誤代碼',$e->getCode(); echo '錯誤文件',$e->getFile(); echo '錯誤行',$e->getLine();//拋出錯誤的行 } ?>