PHP核心進階-面向對象

探討面向對象
1.面向對象是什麼:
面向對象開發簡稱:OOP。它是一種程序設計的規範,同時也是一種開發方法。它的核心思想爲對象化(頂層的設計)、封裝化(代碼集合在一塊兒)、可重用性(減小冗餘)和可擴展性(方便後期維護)。
所謂對象化,就是將程序做爲一個基本的單元,經過命令式的去執行。
所謂封裝化和可重用性,就是將大量可能會冗餘的代碼集合在一塊兒模塊話,只留一些對外接口給開發者調用。
所謂可擴展性,就是造成一種規範,方便後續增長更多功能而不破壞原先的結構。

2.面向對象的核心:
面向對象有三個核心功能:封裝、繼承和多態。
封裝:即將大量實現代碼集合在一塊兒,留出可調用的接口對外。能夠想象一下電腦主機裏,用機箱將主板、CPU、硬盤、內存等封裝在裏面,防止噪音也防止灰塵,只留下一排對外的接口供你調用。
繼承:即將一些程序代碼有規則的複製到另外一個地方使用的方式。能夠理解爲,兒子繼承了父親房產,那麼房產屬於他們公有的部分。固然也會出現一些規則限制,好比父親的微信號或QQ號等,兒子沒法繼承。
多態:即一種事物的多種形態,在程序中體如今不一樣的對象執行同一個方法返回出不一樣的結果。一個很經典的例子:「剪」這個行爲。園丁去用剪這個動做,就是修理花草;理髮師用剪這個動做,就是打理頭髮;總裁去用剪這個動做,就是裁人。這也是面向對象的高級抽象以及精華所在。

3.面向過程和麪向對象的比較:
面向過程,是一種能夠理解爲按照必定的步驟一步一步的完成所須要的功能。這種方式的優勢很明顯,思惟單一,好理解,適合入門開發的人上手起步。缺點也很明顯,各類功能中相同的代碼冗餘太多,擴展性差,維護性差。能夠理解爲剛成立的小公司,只有本身一我的,凡事都是本身親力親爲。
使用面向對象,是一種自上而下的開發方式,頂層作好架構,封裝留出接口,下面具體實現。缺點呢,就是起步稍顯困難一些,在很小的程序感受有點臃腫;優勢是:稍微有些規模的項目,極大的釋放編碼的潛力,可重複性和可擴展性獲得的很大的提高。用現實的例子比如一家有些規模的公司,上層管理者制定計劃方案遞交給底層具體實施的人員去執行操做。

4.面向對象的目的:
1.解決代碼冗餘,沒法可重複性的問題,封裝模塊化後冗餘被大量減小;
2.解決擴展性和維護性,因爲制定了一些的規則後,並不像面向過程那麼隨意的進行編碼,這樣無論是一段時間後的維護擴展仍是換人更新,均可以迅速上手,不須要研讀他人思路的代碼;
3.可團隊化,每一個功能接口實現的規則都一致,多人開發造成了可能。php

 

類與對象
1.建立一個類:
面向對象有具體的語法,首先第一部須要建立一個類。
建立一個類有三個部分組成:第一部分:關鍵字class,第二部分:類名規範第一個字母大寫,第三部分:花括號{}
//建立一個類
class Person
{

}
一個類建立好了,在花括號裏暫時什麼都沒有,這是一個最基本的空類。

2.聲明對象:
(1)使用 new 類名;語法來建立一個對象。數據庫

//建立一個對象
new Person;

建立Person對象,咱們也能夠稱爲:聲明Person對象或實例化Person類。
當實例化後會在內存區域分配一個存儲空間,這個內存叫堆內存,在堆內存分配了一個存儲空間new Person,這個空間存放這個對象的具體內容

(2)查看對象
能夠經過print_r或var_dump函數來獲得這個對象的具體信息。編程

//查看對象
print_r(new Person);
var_dump(new Person);

返回結果:Person Object ( )
返回結果:object(Person)[1]
推薦var_dump,信息更全,更加直觀

(3)建立多個對象
實例化一個類就分配一個內存區域,咱們也能夠聲明多個對象來實現各自獨立的功能。此時,咱們須要給每一個對象關聯上相應的引用地址(或叫對象變量標識)來區分每一個對象。注:有時咱們直接就稱這個引用變量爲對象。數組

//建立多個對象,把對象的引用地址賦值給變量,這個變量也能夠俗稱對象。
$p1 = new Person;
$p2 = new Person;

var_dump($p1);
var_dump($p2);

打印結果:
object(Person)[1]
object(Person)[2]

3.類與對象的探討
(1)類是什麼?是一種抽象組織,比如一臺還沒有啓動的機器,它的結構是固定的統一的;
(2)對象是什麼?是具體實現者,比如操控這臺機器的人,它多是不同的;
(3)當人不去操控這臺機器時,這臺機器毫無做用;
(4)而不一樣的人去操控這臺機器時,獲得的效果也大相徑庭。微信

 

屬性和方法架構

屬性設置
1.屬性其實就是一種變量,只不過它是類裏面的變量。咱們還能夠稱做:字段或成員變量。
2.屬性和變量的設置差不了太多,只不過必須在類裏面設置。最重要的一點是,在屬性前面須要加上一個可見的修飾符,省略修飾符會報錯。修飾符有如下三種形式:
(1).public 表示公開的,在類內、子類和類外都可以讀寫;
(2).protected 表示受保護的,在類內或子類能夠能夠讀寫,類外不行;
(3).privte 表示私有的,只能在類內讀寫,子類和類外不行。
3.對於 public 公共屬性,能夠直接在類外經過對象的賦值與取值操做。
4.使用對象調用類裏面的屬性,能夠經過「->」符號來訪問屬性或方法。請注意,調用屬性的時候,這個變量將不須要再帶「$」符號。框架

<?php
//屬性建立
class Person
{
    public $name = 'Lee';
}

//屬性的賦值和取值操做
$p1 = new Person;
//輸出name
echo $p1->name;

輸出結果:
Leeide

5.咱們在設置屬性的時候,能夠不用當即初始化賦值,只是單純的聲明,將賦值的操做留給後面動態處理。
6.建立多個對象實例後,每一個對象實例分配一個內存區域,而它們之間是沒有關聯的,本身運行着本身的屬性和方法。模塊化

<?php
//屬性建立
class Person
{
    public $name;
}

//對象1
$p1 = new Person;
//賦值
$p1->name = 'Wang';
//取值
echo $p1->name;

//對象2
$p2 = new Person;
$p2->name = 'xixi';
echo $p2->name;

輸出結果:
Wangxixi函數

7.也有種狀況就是將其中的一個對象變量賦值給另一個變量,從而讓兩個變量同時指向一個對象實例。

<?php
//屬性建立
class Person
{
    public $name;
}

//對象1
$p1 = new Person;
//賦值
$p1->name = 'Wang';
//取值
echo $p1->name;

//對象2並無new,不new的話就不會開闢新的內存空間
$p2 = $p1;
echo $p2->name;
$p2->name = 'xixi';
echo $p1->name;

方法設置
1.方法就是類裏面的函數;
2.調用方法和屬性基本一致,而方法和函數的表現形式也是同樣的;

<?php
//方法建立
class Person
{
    public function run()
    {
        return '運行中...';
    }
}

$p1 = new Person;
echo $p1->run();

輸出結果:
運行中...

3.前面的修飾符默認狀況下,能夠省略,那麼默認就是公開的,也有受保護和私有的形式。但規範要求咱們,前面必須加上修飾符,以表示肯定。
4.使用$this 關鍵字,能夠在類的內部調用屬性或方法。

<?php
//方法建立
class Person
{
    public $name = 'Lee';

    public function run()
    {
        //注意:$this表明當前實例化的對象,好比$p1
        return $this->name.'運行中...';
    }
}

$p1 = new Person;
echo $p1->run();

輸出結果:
Lee運行中...

 5.$this 關鍵字在多個實例時,只表示當前實例的屬性值。

 

<?php
//方法建立
class Person
{
    public $name;
    public function run()
    {
        //注意:$this表明當前實例化的對象,好比$p1
        return $this->name.'運行中...';
    }
}

//$p1調用了run,$this就表示$p1
$p1 = new Person;
$p1->name = 'Wang';
echo $p1->run();

//$p2調用了run,$this就表示$p2
$p2 = new Person;
$p2->name = 'XiXi';
echo $p2->run();

輸出結果:
Wang運行中...XiXi運行中...

讀寫方式
1.若是咱們想將屬性進行封裝保護起來,不對外直接訪問操做,也就是設置成private。

<?php
//方法建立
class Person
{
    private $name = 'Wang';

    public function getName()
    {
        return $this->name;
    }
}

$p1 = new Person;
echo $p1->getName();

輸出結果:
Wang

詳解:setName傳一個參數'Wang'給setName()裏的$name的變量,這個變量再傳給屬性$this->name = $name,經過public function getName()這個方法打印出

 

屬性和方法前置修飾符

可見性控制
1.public 是公共對外訪問的修飾符,若是默認不加修飾符,則就是 public。固然,規範要求嚴格必須加上修飾符,以保證程序的健壯性。
2.private 是將屬性或方法私有化,將沒必要要對外的屬性或方法封裝起來,能夠極大減小調用者的使用成本和難度。
3.面向對象的其中一種原則就是:若是沒有必要從讓這個屬性和方法對外訪問,那麼就不要將它公開,只有小量的對外接口便可。固然,若是過分的封裝會致使擴展不順暢,好比一個房子,四面均爲牆,只留了一個狗洞,那麼進出就很不方便。那麼,此時可使用protected 受保護的,它對外也是不可訪問的,但對於擴展類來講是能夠訪問的。
4.使用兩個方法進行對不直接公開的屬性進行賦值和取值,咱們成爲setter和getter。

 

<?php

//受保護的
class Person
{
    protected $name;

    public function setName($name)
    {
        $this->name = $name;
    }

    public function getName()
    {
        return $this->name;
    }
}

$p1 = new Person;
//echo $p1->name;

$p1->setName('Wang');
echo $p1->getName();

打印結果
Wang

靜態修飾符
1.靜態屬性存放在靜態內存區域,屬於公共區域,能夠覆蓋或累計。不須要實例化對象,直接經過「類名::靜態屬性名」方式調用。
2.靜態屬性僅屬於類的屬性,而不屬於對象的屬性。言下之意就是,它直接由類操控,而不併不是對象操控的。因此,不存在會分配多個內存區域,只有一個靜態區。

<?php
//靜態屬性
class Person
{
    public static $name = 'Lee';
}

Person::$name = 'Wang';
echo Person::$name;

打印結果
Wang

3.在普通對象的方法裏怎麼調用靜態方法呢?可使用 self 關鍵「self::靜態屬性名」。

<?php

//靜態屬性
class Person
{
    //私有化,在類外沒法調用
    private static $name = 'Lee';

    public function getStaticName()
    {
        return self::$name;
    }
}

$p1 = new Person();
echo $p1->getStaticName();

打印結果
Wang

 4.靜態屬性經常使用於一些數據的累計,好比統計次數。

<?php
//靜態屬性,數據統計
class Person
{
    private static $count = 0;

    public function addCount()
    {
        self::$count++;
    }

    public function getCount()
    {
        return self::$count;
    }
}

$p1 = new Person;
$p1->addCount();
$p1->addCount();
$p1->addCount();

$p2 = new Person;
$p2->addCount();
$p2->addCount();
$p2->addCount();

echo $p1->getCount();

打印結果:
6

5.靜態方法的功能就簡單明瞭,直接經過「類名::靜態方法名」調用,和麪向過程函數同樣調用。靜態方法經常使用於工具類的那種,不須要實例化直接調用便可的。

<?php

class Tool
{
    public static function back()
    {
        echo "<script>alert('非法操做!')</script>";
    }
}

Tool::back();

執行結果:
彈出一句話:非法操做!

 

魔術方法

PHP中的魔術方法,這種方法由兩個下劃線「__」開頭,具備一些特殊的做用。

1.__set 與__get

1.對於封裝的屬性,咱們經過setter和getter方式來對屬性進行賦值和取值。而實際上,一個類中可能有十幾個或更多的屬性,那麼就必須有十幾組賦值和取值的對外方法。這樣的工做量也是很是龐大的,PHP 提供了這組魔術方法來解決這個問題。

?php
//__set和__get
class Person
{
    private $gender;
    private $age;
    private $work;

    //動態通用賦值,$name表示屬性名稱,$value表示屬性的值
    public function __set($name, $value)
    {
        //注意,這裏的name 帶$符號的,表示的是變量而不是屬性
        //而$name可能就是某個屬性,根據傳遞過來的參數決定
        //若是傳遞的是"gender"和"男",$this->gender = '男';
        $this->$name = $value;
    }

    //動態通用取值
    public function __get($name)
    {
        return $this->$name;
    }
}

$p1 = new Person;
$p1->gender = '男';  //這裏至關於$p1->__set('gender', '男');
$p1->age = 30;
$p1->work = '醫生';
echo $p1->gender.$p1->age.$p1->work;

返回值:
男30醫生

2.__set和__get主要是對沒法直接訪問的屬性起做用,若是是公共的屬性private,它會直接賦值取值。
3.以前獲取屬性方式是$this->name,這種形式。若是用__set則是$this->$name,$name 變量經過參數傳遞,多是$name表示的是gender、age或work,這是動態的。

 

2.__construct構造
1.構造方法或構造函數是一種特殊的類的方法,早期使用只須要和類同名的方法便可實現構造方法。
2.而目前爲了防止混淆,使用了魔術方法__construct 來取代類同名的方法,可讀性更加的高。
3.構造方法的參數,由實例化時進行傳遞。

<?php

//構造方法
class Person
{
    public function __construct($name)
    {
        echo $name.'魔術方法構造!';
    }
}

$p1 = new Person('XiXi');
$p2 = new Person('Wang');

返回值:
XiXi魔術方法構造!Wang魔術方法構造!

4.因而可知,構造方法的目的是當對象實例化後,是對整個對象數據的初始化工做。就好像機器啓動後,各個環節進行充能就位同樣。
5.對於沒有參數傳遞時,之後默認的寫法就留着空括號便可,方便之後擴展,不須要刻意的去掉括號。
//不傳參
$p1 = new Person();

 

3.__destructor()析構
1.析構方法和構造方法正好相反,當對象的方法所有執行完畢後自動調用析構方法。這種方法通常用於清理和銷燬操做,好比清理內存,銷燬數據庫連接等。固然,這個方法不太經常使用,由於如今都自動清理(俗稱垃圾回收)。

//析構方法
public function __destruct()
{
echo '析構方法,運行結束後銷燬還原!';
}

 

4.__call 方法
1.因爲某種緣由,有時可能調用了不存在的方法。這樣,會報出一個找不到相關方法的錯誤

//調用不存在的方法語法:
class Person
{
}
$p1 = new Person();
$p1->run();
2.可使用__call 魔術方法來屏蔽錯誤調用,當調用不存在方式時,則執行這個魔術方法。須要填寫兩個參數:$name 表示方法名;$arguments 表示參數列表。
<?php
//調用不存在的方法
class Person
{
    //沒有執行到指定方法則會自動執行__call魔術方法
    public function __call($name, $args)
    {
        echo $name.'方法不存在!';
        echo '<br>';

        foreach ($args as $value) {
            echo $value;
        }
    }
}

$p1 = new Person();
$p1->run('Mr.', 'Lee', 25);

返回值:
run方法不存在!
Mr.Lee25

3.相對於了還有一種是靜態方法調用不存在時,使用__callStatic魔術方法。

<?php
//調用不存在的方法
class Person
{
    //沒有執行到指定方法則會自動執行__call魔術方法
    public static function __callStatic($name, $args)
    {
        echo $name.'方法不存在!';
        echo '<br>';

        foreach ($args as $value) {
            echo $value;
        }
    }
}
Person::tool('Mr.', 'Lee', 25);

返回值:
tool方法不存在!
Mr.Lee25

5.__isset 方法
1.當不可對外訪問的屬性在類外調用了 isset/empty 方法判斷是否設置或有值時,會自動調用__isset 魔術方法,來避免錯誤和返回判斷結果。

<?php
//__isset判斷非公開屬性是否存在或是否有值
class Person
{
    private $name = 'Wang';

    public function __isset($name)
    {
        return isset($this->$name);
    }
}

$p1 = new Person();

echo isset($p1->name);

返回值:
1

詳解:isset執行($this->$name)的時候發現private $name = 'Wang';是私有的執行不到,而後會自動跳到__isset($name)去執行

2.反之,還有一個__unset 魔術方法,即:調用unset不對外的屬性時觸發。

<?php
//__isset判斷非公開屬性是否存在或是否有值
class Person
{
    private $name = 'Wang';

    public function __isset($name)
    {
        return isset($this->$name);
    }

    public function __unset($name)
    {
        unset($this->$name);
    }
}

$p1 = new Person();

echo empty($p1->name);

unset($p1->name);

返回值:
1

6.__toString 方法
1.原則上,是沒法直接輸出(echo)對象信息的。而經過 var_dump 只能輸出系統自己格式化後的信息。有時,咱們完成一個類,相對調用的開發這顯示出這個類全部屬性和方法具體的信息介紹,可使用__toString 魔術方法來自定義對象的信息輸出格式。

<?php
//輸出對象自定義信息
class Person
{
    private $name = 'XIXI';

    public function run()
    {
        return 'running...';
    }

    public function __toString()
    {
        $str = '';
        $str .= '私有字段:name,用於姓名的賦值和取值<br>';
        $str .= '公共方法:run(),用於對外輸出名稱';
        return $str;
    }
}
$p1 = new Person();

echo $p1;

私有字段:name,用於姓名的賦值和取值
公共方法:run(),用於對外輸出名稱
2.魔術方法的原理很簡單,當沒法執行或報錯時就會調用這個方法。

 

對象中使用繼承(解決代碼中大量冗餘和擴展的便利性)

繼承的概念
1.繼承是類與類之間相互結合的關係。通俗一點就是說:兒子(子類)從父親(父類)或母親那邊繼承了外貌(屬性)和性格(方法)。
2.子類能夠從父類那裏繼承或擴展,而父類並不知道子類繼承了它。因此,一個父類能夠有被多少個子類繼承沒有限制。
3.子類繼承了父類的全部特性,並在這個基礎上進行加強,最終獲得獨具一格的擴展。
4.繼承的關鍵字是:extends。

<?php
//父類(基類)
class Father
{
    public $name = 'Mr.Wang';

    public function run()
    {
        return $this->name.'running...';
    }
}

//子類(派生類)
class Children extends Father
{
    //1.能夠把非私有的屬性繼承下來
    //2.能夠把非私有的方法繼承下來
}

$c1 = new Children();
echo $c1->name;
echo $c1->run();

返回值:
Mr.WangMr.Wangrunning...

<?php
//父類
class Father
{
    protected $name = 'Mr.Wang';

    protected function run()
    {
        return $this->name . 'running...';
    }
}

//子類
class Children extends Father
{
    //受保護的,是能夠繼承下來的,可是不能類外訪問
    public function test()
    {
        echo $this->name;
        echo $this->run();
    }
}

$c1 = new Children();
$c1->test();

返回值:
Mr.WangMr.Wangrunning...

5.子類繼承了父類的屬性和方法,那麼實例化子類對象後,可直接調用屬性和方法。
6.若是當父類的屬性或方法設置爲私有:private 時,那麼就沒法被子類繼承。 

<?php
//父類
class Father
{
    private $name = 'Mr.Lee';

    private function run()
    {
        return $this->name . 'running...';
    }
}

//子類
class Children extends Father
{
    //私有的屬性和方法沒法繼承
    public function test()
    {
        //echo $this->name;
        //echo $this->run();
    }
}

$c1 = new Children();
$c1->test();

7.若是父類建立了構造方法,而子類沒有建立構造方法,則聲明子類會自動執行父類的構造方法。

<?php
//父類
class Father
{
    public function __construct()
    {
        echo '父類構造';
    }
}

//子類
class Children extends Father
{
    
}

$c1 = new Children();

打印:
父類構造

覆蓋重寫
1.若是子類建立了構造方法,則父類的構造方法會被覆蓋(重寫),按規範要求須要在子類構造方法裏調用父類構造。
2.使用 parent::__construct()方式能夠調用父類的構造方法,parent 表示父類,也可使用父類名::__construct()調用,但推薦使用 parent。
3.普通的父類方法,也能夠被子類重寫,也可使用parent關鍵字調用父類方法。

<?php
//父類
class Father
{
    public function __construct()
    {
        echo '父類構造';
    }
}

//子類
class Children extends Father
{
    public function __construct()
    {
        //調用父類構造方法:父類名::__construct()
        //Father::__construct();
        //推薦parent表明父類
        //構造方法主要做用是初始化數據,父類的初始化也必須運行,因此子類構造須要調用父類構造
        parent::__construct();
        echo '子類構造';
    }
}

$c1 = new Children();

打印:
父類構造子類構造

3.普通的父類方法,也能夠被子類重寫,也可使用parent關鍵字調用父類方法。
4.通常來講子類調用自身的方法使用$this 便可,也可使用 self::方法()來調用自身重寫的方法,來強調不是調用父類方法。

<?php
//父類
class Father
{
    public function run()
    {
        echo '父類run';
    }
}

//子類
class Children extends Father
{
    //1.父類方法被繼承,this就調用父類的方法
    //2.子類方法重寫了,this就調用子類的方法
    //3.this可使用self來強調,我是調用的子類。
    public function run()
    {
        echo '子類run';
    }

    public function test()
    {
        //$this->run();
        self::run();
    }
}

$c1 = new Children();
$c1->test();

子類run
6.繼承只支持單繼承,也就是說,不能夠同時繼承多個父類

 

面向對象中的抽象類和其它的抽象方法

抽象類
1.抽象類提供了一種機制,父類中的抽象方法並無實現(僅提供方法聲明),而是必需要求子類重寫這個方法去實現它。
2.若是一個類中有抽象方法,那麼此類必須聲明成抽象類。
3.抽象類和抽象方法的聲明修飾符是:abstract。
4.抽象方法聲明的修飾符不能夠是private的,必需要子類繼承,private沒法繼承實現,而protected(受保護的)或public(公有的)都可。
5.抽象類沒法實例化,也就是說,抽象方法的修飾符是protected和public並無什麼區別。因此不少狀況下,咱們發現都是省略不寫的,按照規範來講,建議寫上去可讀性高一些。

<?php
//抽象類
abstract class Person
{
//抽象方法
    abstract protected function run();
}
//子類
class Man extends Person
{
    //實現抽象方法
    public function run()
    {
    echo '運行...';
    }
}
$m1 = new Man();
echo $m1 ->run();

打印:
運行...

6.抽象方法裏面也可使用普通的屬性和普通方法,在子類繼承下來,正常使用。

<?php
//抽象類
abstract class Person
{
    protected $name = 'Wang';
    public function getName()
    {
        return $this->name;
    }

//抽象方法
    abstract protected function run();
}
//子類
class Man extends Person
{
    //實現抽象方法
    public function run()
    {
         return '運行...';
    }
}
$m1 = new Man();
echo $m1 ->run();
echo $m1->getName();

打印:
運行...Wang

7.若是父類的抽象方法有參數,那麼子類實現時,也必須跟着實現這兩個參數。

<?php
//抽象類
abstract class Person
{
    protected $name = 'Wang';
    public function getName()
    {
        return $this->name;
    }

//抽象方法
    abstract protected function run($key, $value);
}
//子類
class Man extends Person
{
    //實現抽象方法
    public function run($key,$value)
    {
          return '運行...'.$key.$value;
    }
}
$m1 = new Man();
echo $m1 ->run(1,'Wang');
echo $m1->getName();

返回值:
運行...1WangWang
8.爲什麼要設計抽象方法這種只提供方法名和參數的機制呢?這就是面向對象的一種思惟,頂層設計出相應的規範,而後底層子類按照這個規範進行實現。能夠提高代碼的可讀性、維護擴展性和協做性。
9.固然,抽象類不僅僅能夠提供規範讓子類實現,也能夠本身實現讓子類繼承直接使用,很是的靈活。

 

面向對象中的接口

interface接口的使用
1.接口有兩種概念:第一種是表示提供對外服務的出口,好比不少網站提供的API開發接口,支付寶、QQ登陸等等。而第二種是PHP的一個關鍵字語法:interface接口。
2.接口和抽象類很像,接口定義一條規範,總裁把各類標準計劃設計好,讓底層員工去具體實現。
3.接口比抽象類作的更加完全,它規定了全部方法都是抽象方法,只聲明不實現。而且不須要在前面加上abstract。
4.接口的抽象方法,修飾符必須是 public,其它修飾符直接報錯。
5.子類實現接口(相似繼承),必須重寫接口的抽象方法。

<?php
//接口
interface Person
{
    //抽象方法(作一個規範)
    public function run();
}

//子類實現接口
class Man implements Person
{
    //強制性實現抽象方法
    public function run()
    {
        echo '運行...';
    }

}

$m1 = new Man();
$m1->run();

返回:
運行...

6.接口不能設置屬性,只能設置常量
常量使用調用:

<?php

class Person
{
    const PI = 3.14;
}

echo Person::PI;

打印:
3.14

子類實現接口調用:

<?php
//接口
interface Person
{
    const PI = 3.14;

    //抽象方法
    public function run();
}

//echo Person::PI;
//子類實現接口
class Man implements Person
{
    //實現父類接口的抽象方法
    public function run()
    {

    }
}

echo Man::PI;

返回:
3.14

7.子類能夠實現多個接口。

<?php
//接口1
interface Person
{
    //抽象方法
    public function run();
}

//接口2
interface Computer
{
    //抽象方法
    public function sleep();
}

//子類實現兩個接口
class Man implements Person,Computer
{
    public function run()
    {

    }

    public function sleep()
    {

    }
}

//接口能夠多實現,繼承只能單繼承

 

面向對象中的多姿態

1.用術語來講明多態性,就是經過多種狀態或階段描述相同對象的編程方法。它的意義在於,實際開發中,咱們只要關心接口或父類編程,並不須要關心一個對象所屬於的具體類。

<?php
//人抽象類(父類)
abstract class Person
{
    //作兩個規範聲明
    abstract protected function type();
    abstract protected function content();
}
//理髮師類(子類1)
class Barber extends Person
{
    public function type()
    {
        return '理髮師';
    }
    //content要操做的內容
    public function content()
    {
        return '修理頭髮';
    }
}

//園丁類(子類2)
class Gardener extends Person
{
    public function type()
    {
        return '園丁';
    }

    public function content()
    {
        return '整理花卉';
    }
}

//總裁類(子類3)
class President extends Person
{
    public function type()
    {
        return '總裁';
    }

    public function content()
    {
        return '裁人';
    }
}

//剪刀類(行爲類)
class Cut
{
    //這裏的run須要傳遞一個參數
    //傳遞一我的的對象,好比園丁對象或理髮師對象
    //能夠寫$object表示一個對象,可是用$person更具有語義
    //由於我不知道是園丁仍是理髮師,仍是總裁,可是他們都是人,那麼用$person更加合理
    public function run($person)
    {
        return $person->type().'正在'.$person->content().'中...';
    }
}

//實例化三我的(理髮師,園丁,總裁)的對象
$b = new Barber();
$g = new Gardener();
$p = new President();

//實例化行爲對象
$cut = new Cut();
//這裏傳對象,就是根據不一樣的對象,去執行相同的方法,最終致使多態性
echo $cut->run($p);

傳的總裁$p這個對象返回:
總裁正在裁人中...

7.從上面的例子看出,多態就是同一類對象在運行時的具體化。不一樣的人物對象做爲參數傳入剪類,就會調用傳入類的方法。
8.而抽象父類,規定了這些人物類必須實現一樣的方法名,爲多態實現了可能。
9.其實,多態的本質仍是條件判斷語句。

 

類和對象的檢測機制

類和對象的檢測機制,這種機制可讓程序檢測對象的特性,包括對象名稱、
類的檢測
1.使用 class_exists()函數來肯定一個類是否存在。
//判斷 Person 類是否存在
echo class_exists('Person');
2.使用 get_declared_classes()函數返回目前可用類的數組列表,使用var_dump沒法全面打印。
//輸出可用類列表
var_dump(get_declared_classes());
3.使用 get_class_methods()函數返回類中全部的方法,包括繼承下來的。
//輸出類中的方法
var_dump(get_class_methods('Person'));
4.使用 get_class_vars()函數返回類中全部的屬性,包括繼承下來的。
//輸出類中的屬性
var_dump(get_class_vars('Person'));

<?php
//
class Person
{
    public $name = 'Mr.Wang';

    public function run()
    {
        return '運行...';
    }
}

$p1 = new Person();

echo class_exists('Person');
var_dump(get_declared_classes());
var_dump(get_class_methods('Person'));
var_dump(get_class_vars('Person'));

注意:不對外的方法和屬性獲取不到。

 

對象檢測
1.使用 get_class()函數來獲取對象所屬的類。
//獲取對象所屬的類
echo get_class($p1);
2.使用 method_exists()函數來判斷對象的某個方法是否存在。
//判斷對象的某個方法是否存在
echo method_exists($p1, 'run');
3.使用 get_object_vars()函數來獲取對象中的屬性列表。
//經過對象參數返回屬性了列表
var_dump(get_object_vars($p1));
4.使用 get_parent_class()函數來獲取父類的名稱。
//返回父類的名稱,傳參能夠是類名或對象名
echo get_parent_class('Person');

<?php
//父類
class A
{

}
//
class Person extends A
{
    public $name = 'Mr.Wang';

    public function run()
    {
        return '運行...';
    }
}

$p1 = new Person();

echo get_class($p1);
echo method_exists($p1, 'run');
var_dump(get_object_vars($p1));
echo get_parent_class('Person');

打印:
Person1
array (size=1)
  'name' => string 'Mr.Wang' (length=7)
A

 

檢查自省的反射API,是一種對類和對象的自查機制(只不過是面向對象方式的)
使用反射
1.建立一個基本的類,包含了屬性和方法
2.建立一個獲取對象的反射類。
3.獲取屬性列表和方法列表。

<?php
//建立一個基本的類
class Person
{
    private $name;
    private $age;

    public function __set($name, $value)
    {
        $this->$name = $value;
    }

    public function __get($name)
    {
        return $this->$name;
    }
}
//實例化
$p = new Person();

//聲明一個經過傳遞對象參數的反射類,這個類是系統內部類
$r = new ReflectionObject($p);

//屬性列表
foreach ($r->getProperties() as $value) {
    var_dump($value);
}

//方法列表
foreach ($r->getMethods() as $value) {
    var_dump($value);
}

打印結果:
//打印屬性列表
object(ReflectionProperty)[3]
  public 'name' => string 'name' (length=4)
  public 'class' => string 'Person' (length=6)

object(ReflectionProperty)[4]
  public 'name' => string 'age' (length=3)
  public 'class' => string 'Person' (length=6)
//打印方法列表
object(ReflectionMethod)[3]
  public 'name' => string '__set' (length=5)
  public 'class' => string 'Person' (length=6)

object(ReflectionMethod)[5]
  public 'name' => string '__get' (length=5)
  public 'class' => string 'Person' (length=6)

 

4.使用傳遞類名方式的反射類。

<?php
//建立一個基本的類
class Person
{
    private $name;
    private $age;

    public function __set($name, $value)
    {
        $this->$name = $value;
    }

    public function __get($name)
    {
        return $this->$name;
    }
}

$r = new ReflectionClass('Person');
//獲取屬性列表
foreach ($r->getProperties() as $value) {
    var_dump($value);
}
//獲取方法列表
foreach ($r->getMethods() as $value) {
    var_dump($value);
}

打印:
//獲取屬性列表
object(ReflectionProperty)[2]
  public 'name' => string 'name' (length=4)
  public 'class' => string 'Person' (length=6)

object(ReflectionProperty)[3]
  public 'name' => string 'age' (length=3)
  public 'class' => string 'Person' (length=6)
//獲取方法列表
object(ReflectionMethod)[2]
  public 'name' => string '__set' (length=5)
  public 'class' => string 'Person' (length=6)

object(ReflectionMethod)[4]
  public 'name' => string '__get' (length=5)
  public 'class' => string 'Person' (length=6)


反射的做用
1.反射能夠用於生成類或對象的文檔,提供開發者閱讀;
2.可使用反射功能開發出各類插件系統,由於這種類型的系統老是要檢測;
3.不少框架使用反射的特性動態的加載須要的類,判斷方法等。

 

命名空間對類進行分裝

命名空間概念
1.命名空間是一種封裝事物的方法,就好像系統中的文件夾系統,若是文件名相同,能夠存放在不一樣的文件夾以免衝突。
2.命名空間就是爲了防止類與類之間可能產生的衝突,而制定的這套機制。
3.還能夠防止命名空間和PHP系統的類等發生衝突。
4.防止那些爲了緩解衝突,聲明很長的類名,提升可讀性。
5.命名空間不必定非要和目錄結構的名稱同樣,但同樣的話,可讀性會高。

定義命名空間
1.首先在第一個文件設置一個帶有命名空間的類文件。

1.php

<?php
//設置命名空間(虛擬目錄App)
namespace App;

class Person
{
    public function run()
    {
        return '1運行...';
    }
}

2.建立第二個文件2.php,引入1.php時,實例化Person。
2.php

<?php

require '1.php';
require '3.php';

$p1 = new \App\Person();
echo $p1->run();

$p2 = new \Home\Person();
echo $p2->run();

3.若是在3.php 中再建立一個Person類,只要命名空間不一樣,就不會衝突了。
3.php

<?php
//設置命名空間(虛擬目錄Home)
namespace Home;


class Person
{
    public function run()
    {
        return '3運行...';
    }
}

4.不一樣命名空間的同名類,不會產生衝突。
//不衝突
$p1 = new \App\Person();
$p2 = new \Home\Person();

運行http://192.168.3.62/xiangmu3/2.php
1運行...3運行...

 

5.若是說,自己創建了命名空間機制,但並無重名類的問題,可使用use語法。
4.php

<?php

require '1.php';
require '3.php';

use App\Person;

$p1 = new Person();
echo $p1->run();

運行http://192.168.3.62/xiangmu3/4.php
1運行...

6.若是有重名的,就不適合使用 use 語法了。
7.也能夠像文件系統那樣定義多個子命名空間,讓層次結構更加細化。
1.php

<?php
//設置命名空間(虛擬目錄)
namespace App\Bev\Org;

class Person
{
    public function run()
    {
        return '1運行...';
    }
}

5.php

<?php

require '1.php';

use App\Bev\Org\Person;

//$p1 = new \App\Bev\Org\Person();
$p1 = new Person();
echo $p1->run();

運行http://192.168.3.62/xiangmu3/5.php
1運行...

8.使用__METHOD__常量能夠獲取方法的所有路徑。
9.使用__FUNCTION__常量能夠獲得單純的方法名
10.能夠在有命名空間的文件下使用__NAMESPACE__常量獲取命名空間路徑。

1.php

<?php
//設置命名空間(虛擬目錄)
namespace App\Bev\Org;

class Person
{
    public function run()
    {
        return '1運行...'.__METHOD__.__FUNCTION__.__NAMESPACE__;
    }
}

5.php

<?php

require '1.php';

use App\Bev\Org\Person;

//$p1 = new \App\Bev\Org\Person();
$p1 = new Person();
echo $p1->run();

運行http://192.168.3.62/xiangmu3/5.php
分別獲得的返回值是:
1運行...
App\Bev\Org\Person::run
run
App\Bev\Org

11.使用use別名來簡化重名類的衝突問題。
1.php

<?php
//設置命名空間(虛擬目錄)
namespace App\Bev\Org;

class Person
{
    public function run()
    {
        return '1運行...'.__METHOD__.__FUNCTION__;
    }
}

3.php

<?php
//設置命名空間
namespace Home\Go\Back;


class Person
{
    public function run()
    {
        return '3運行...';
    }
}

7.php

<?php
require '1.php';
require '3.php';

use App\Bev\Org\Person as One;
use Home\Go\Back\Person as Two;

$p1 = new One();
echo $p1->run();

$p2 = new Two();
echo $p2->run();

http://192.168.3.62/xiangmu3/7.php
1運行...App\Bev\Org\Person::runrun3運行...

 

異常類

異常的概念
1.異常就是在可能發生錯誤的代碼區域使用異常語法包裹住,當程序代碼發生可能出現的錯誤,及時拋出異常的一種策略。
2.異常通常來講須要如下四個步驟實現:
(1)程序嘗試執行一些代碼;
(2)若是執行失敗,則拋出一個異常;
(3)捕獲該異常;
(4)清理執行出現異常代碼而遺留的資源。

使用異常類
1.PHP 中內置一個基本的異常類(Exception),基本的異常類用於在腳本發生異常時創建異常對象,該對象能夠存儲、拋出和捕獲異常信息。
2.能夠先執行一個會產生錯誤的代碼。
若是被除數爲0的話返回被除數不能爲零!

<?php

$a = 5;
$b = 0;

if ($b == 0) {
    exit('被除數不能爲零!');
} else {
    echo $a / $b;
}

返回值:
被除數不能爲零!

3.若是使用異常方式,須要使用try catch語法。throw 是手動拋出異常,若是PHP有相關處理此異常的異常類,就會自動拋出,而不須要判斷。

<?php

$a = 5;
$b = 0;

try {
    //throw new Exception手動拋出異常
    if ($b == 0) throw new Exception();
    //這句話可能會有錯誤
    echo $a / $b;

} catch (Exception $e) {
    echo '被除數不得爲零!';
}

返回值:
被除數不得爲零!

4.錯誤提示能夠經過構造傳參傳遞。
//經過構造傳參拋出錯誤信息

<?php

$a = 5;
$b = 0;

try {
    //手動拋出異常
    if ($b == 0) throw new Exception('被除數不得爲零');
    //這句話可能會有錯誤
    echo $a / $b;

} catch (Exception $e) {
    echo $e->getMessage();
}

返回值:
被除數不得爲零

 

5.異常類有不少方法能夠調用。
(1).getMessage():返回傳遞給構造方法的信息;
(2).getCode():返回傳遞給構造方法的代碼;
(3).getFile():返回產生異常代碼文件的完整路徑;
(4).getLine():返回代碼文件中產生代碼的行號;
(5).getTrace():返回一個包含產生異常代碼回退路徑的數組;

<?php

$a = 5;
$b = 0;

try {
    //手動拋出異常
    if ($b == 0) throw new Exception('被除數不得爲零');
    //這句話可能會有錯誤
    echo $a / $b;

} catch (Exception $e) {
    echo $e->getMessage();
    echo $e->getCode();
    echo $e->getLine();
    echo $e->getFile();
}

返回值:.getMessage():被除數不得爲零.getCode():0.getLine():8.getFile():E:\Program\www\xiangmu3\1.php

相關文章
相關標籤/搜索