PHP基礎教程十之靜態屬性和靜態方法

本節講解的內容

  • 靜態屬性和靜態方法
  • 訪問修飾符
  • 單例模式
  • 魔術方法
  • 類的自動加載

前言

在上一節中,我們介紹了面向對象的基本使用,但是上節的知識,在實際中還有問題是解決不了的,比如我們去買票,有一個總票數(定義票這個屬性),來一個人買一張票(票-1),但是我們每次創建一個對象,根據對象在內存中的方式,都是重新創建一個總票數,這樣是不合理,這裏我們就使用到了靜態這個概念,在PHP中類中的靜態分爲兩種:

  1. 靜態屬性
  2. 靜態方法

靜態屬性

靜態屬性是該類的所有對象共享的變量,任何一個該類的對象去訪問它時,取到的都是相同的值,同樣任何一個該類的對象去修改它時,修改的也是同一個變量

<?php
    class Ticket{
        public static $number = 100; //設置總的票數是100;
        public $name ; 

        public function __construct($name){
            $this-> name = $name;
        }
        //定義一個方法進行買票
        public function sellTicket(){
            echo $this-> name . '買票了<br>';
            self::$number--; //每調用一次方法總票數就減一。
        }
    }

    $people1 = new Ticket('小白');
    $people2 = new Ticket('小明');
    $people3 = new Ticket('小華');
    $people4 = new Ticket('小張');
    //每個人進行買票
    $people1 -> sellTicket();
    $people2 -> sellTicket();
    $people3 -> sellTicket();
    $people4 -> sellTicket();

    echo Ticket::$number; //在類的外部通過類名訪問靜態屬性。
    ......結果........
    小白買票了
    小明買票了
    小華買票了
    小張買票了
    96

在上面的代碼中可以看到靜態屬性的定義方式。

訪問修飾符  static  $靜態屬性名字 = 初始化值;

靜態屬性只能定義在類的內部。

靜態屬性的訪問

在類外面

在類外面我們也是可以訪問類中的靜態屬性的,就像上面寫的那樣通過類名來直接訪問(只有權限修飾符是public的時候才能這樣)Ticket::$number;其中::是範圍解析符。

在類的外部還可以通過對象來對靜態屬性進行訪問

$people4::$number;

通過類名進行訪問是通過範圍解析符::進行訪問。

在類裏面

上面的代碼中,我們可以看到,在類中我們通過self::$靜態屬性名進行訪問。除了這種方式,在類中還有一種方式來進行訪問。

Ticket::$number--;

通過類名來訪問。而推薦使用的格式是通過self這種方式,因爲這種方式當我們的類名改變後,也不用修改。那麼self和$this有什麼區別呢?

$this和self的區別

其實在上節中講到$this,是指向當前的對象的,而這裏的self是指向當前類,一個指向對象,一個指向類,指向不同。同時他們兩個的使用方式不一樣self是兩個::,$this是->。但是它們兩個的適用範圍是一樣的,都是在類的內部使用。

靜態屬性的使用

在上面我們只是講解了靜態屬性的定義方法以及使用方法,至於什麼時候時候需要使用到靜態屬性。當我們在項目開發中,需要讓所有的對象共享一份數據時,我們就考慮使用靜態屬性。

靜態屬性也是一個屬性,那麼它與普通屬性的區別是:

  • 屬性加上static這個關鍵字,就會變成靜態屬性。
  • 靜態屬性的屬於類的,所有對象共享的屬性
  • 普通屬性是屬於單一對象的。

注意,就像上面的一樣,在非靜態方法中可以訪問靜態屬性。

靜態方法

上面講到靜態屬性,那麼接下來就講一講靜態方法。

<?php
    class Ticket{
        public static $number = 100; //設置總的票數是100;
        public $name ; 

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

        public static function sayHello(){
            echo '這是靜態方法<br>';
        }

        public function info(){
            //在類的內部使用靜態方法
            self::sayHello(); //通過self訪問
            Ticket::sayHello();//通過類名的方式進行訪問
        }
    }

    $people1 = new Ticket('小白');
    $people1 -> info();
    $people1::sayHello(); //在類的外部通過對象名進行訪問
    Ticket::sayHello();  //通過類型進行訪問。
    ......結果........
    這是靜態方法
    這是靜態方法
    這是靜態方法
    這是靜態方法

靜態方法的定義方式是通過關鍵字static來定義的:

訪問修飾符 static function 方法名(參數列表){
    code....
}

靜態方法的訪問

在類外面

靜態方法在類外邊的訪問形式和訪問靜態屬性的方法是一樣的(權限修飾符只有是public纔可以在外部訪問)。

  • 通過類名::靜態方法名進行訪問
  • 通過對象名::靜態方法名(不推薦)
  • 通過對象名->靜態方法名。也就是訪問方法的形式。

在類裏面

在類裏面訪問靜態方法的方式和訪問靜態屬性的方法也是一樣的

  • self::靜態方法名
  • 類名::靜態方法名

靜態方法的使用

那麼我們在什麼情況下使用靜態方法呢?我們可以在操作靜態屬性的時候使用靜態方法。

  • 當我們需要操作靜態屬性時,我們考慮使用
  • 在我們php開發中,經常會使用到一些模式,比如單例模式,工廠模式,觀察者模式等,都使用使用靜態方法.

注意:靜態方法不能訪問非靜態屬性

訪問修飾符

在上面的代碼中和說明中,我們可以看到不管是在屬性的前面還是在方法的前面都有一個public,這個public就是訪問修飾符其中的一種。訪問修飾符可以說是實現對象封裝的方法。

訪問修飾符的分類及區別

在PHP中訪問修飾符可以分爲三中

  1. public 在上面的代碼中我們都是使用的public,使用這種這個關鍵字修飾的屬性和方法,不管在類的內部還是在類的內部都是可以訪問的。
  2. protected(受保護的)如果使用這個關鍵字修飾,那麼在類的外部是不能訪問。只能在類的內部進行訪問。

    <?php
    
        class Cat{
            public $name;
            protected $age;
            public function __construct($name,$age){
                $this -> name = $name;
                $this -> age = $age;
            }
    
        }
    
        $cat = new Cat('小白',4);
        echo $cat -> name; //在類的外部訪問public
        echo '<br>';
        echo $cat -> age; //在類的外部訪問protected修飾的屬性。
    ......結果.....
    小白
    
    Fatal error: Cannot access protected property Cat::$age in D:\mywamp\Apache24\htdocs\zendstudio\yunsuanfu\xiushifu.php on line 16
    

    錯誤的信息是說不能訪問protected修飾的屬性。

  3. private(私有的),只能在類的內部使用,在外部使用會報和上面一樣的錯誤。

這三種,後面兩種看起來作用一樣,都是隻能在類內部使用,那又有什麼區別呢?現在看來,並沒有區別,但是學過類的繼承,那麼這兩種還是有區別的。
這裏寫圖片描述
訪問修飾符的使用:

  • 成員屬性必須制定訪問修飾符,不然會報錯
  • 方法前面可以不寫修飾符,默認是public
  • 靜態屬性可以不指定訪問修飾符,默認是public

單例模式

上面講解到我們什麼時候使用到靜態方法。在一些設計模式中,我們可以使用到靜態方法和靜態屬性。

設計模式:是一套被反覆使用、多數人知曉的、經過分類編目的、代碼設計經驗的總結。使用設計模式是爲了可重用代碼、讓代碼更容易被他人理解、保證代碼可靠性。 毫無疑問,設計模式於己於他人於系統都是多贏的;設計模式使代碼編制真正工程化;設計模式是軟件工程的基石脈絡,如同大廈的結構一樣。(百度)

在開發的時候,我們有這樣的需求,在代碼中我們創建一個對象,但是我們希望在一個項目中,這個對象實例只能有一個,不能創建多個對象,從而實現對數據庫等資源的保護。這時候就使用到單例模式。

<?php

    class DaoMysql{
        public $link; //模擬數據庫連接
        private static $instance;//當前類的對象。

        private function __construct(){
            echo '數據庫連接<br>';
        }

        public static function getInstance(){
            if(self::$instance == null){
                self::$instance = new DaoMysql();
            }
            return self::$instance;
        }

        public function insertSql(){
            echo '添加數據<br>';
        }
    }

    $daoMysql = DaoMysql::getInstance();
    $daoMysql -> insertSql();
    $daoMysql2 = DaoMysql::getInstance();
    $daoMysql2 -> insertSql();
    ......結果.......
    數據庫連接
    添加數據
    添加數據

1. 既然是單例模式,那麼就不能在外部創建對象,這就要把構造函數用private修飾(創建對象要調用構造函數,這裏把構造函數私有化,調用不起來),這樣在外部就不能創建對象。
2. 我們在類裏面創建了一個對象的引用$instance,在類裏面創建對象,這是允許的。
3. 在類中定義一個靜態方法,我們就是通過這個方法來創建對象,通過類名::方法名進行創建,進去後先判斷$instance是否爲空,只有如空的時候我們才進行創建。然後返回對象。
4. 因爲要在靜態方法中訪問屬性,那麼這個屬性就應該是靜態的屬性。
5. 在類的外部通過類::靜態方法名進行對象的創建。
6. 在結果中我們可以看到我們有兩個對象,但是構造方法在第二次沒有執行,說明對象沒有創建。

雖然在上面我們做了很多限制,但是在PHP中還是有方法的到更過的對象,克隆和繼承。

對象類型運算符

在上面的靜態方法中判斷對象是否創建還有一種方法。

if(!(self::$instance instanceof self)){
                self::$instance = new DaoMysql();
}

其中instanceof就是類型運算符。 根據幫助文檔,它有幾個作用

  1. 用於確定一個 PHP 變量是否屬於某一類 class 的實例:
  2. 可用來確定一個變量是不是繼承自某一父類的子類的實例:
  3. 也可用於確定一個變量是不是實現了某個接口的對象的實例:

上面的代碼中self代表當前的類。instanceof判斷前面的變量是否是後面類的實例,然後取反。

魔術方法

在PHP中有一些定義在類中的神奇的方法,稱爲魔術方法。具體的魔術的方法的使用可以看另外一篇博客
PHP的魔術方法

類的自動加載

在前面我們講過文件的引入,使用include和require這兩種類型。在開發中我們有時需要引入大量的文件,可以是10個,也可能是20個,如果還是使用原來的方法,累人。

在 PHP 5 中,不再需要這樣了。可以定義一個 __autoload() 函數,它會在試圖使用尚未被定義的類時自動調用
而我們在寫類的時候,一般都是一個類一個文件,而文件的名字我們一般是類名.class.php的格式。

<?php
    //自動加載的方法,當我們使用這個文件不存在的類的時候,就會自動加載。
    function __autoload($class_name){
        require_once './' . $class_name . '.class.php';
    }

    $dao = new Dao('小白',5);
    $cat = new Cat('小花',2);
    $dao -> eat();
    $cat -> eat();

__autoload($類名),在個函數不是寫在類中的,所以前面是沒有權限修飾符。

上面的自動加載方式是有侷限性的,當文件是在不同的文件夾中的時候,這種方法顯然是不行的。這時候可以創建一個數組,把類名當做鍵,對應的路徑當成值,進行存儲。自動加載的時候就能正確的引入。

<?php

    $path = array(
            'Dao' => './dao/Dao.class.php',
            'Cat' => './cat/Cat.class.php'
        );



    //自動加載的方法,當我們使用這個文件不存在的類的時候,就會自動加載。
    function __autoload($class_name){
        global $path;
        require_once $path[$class_name];
    }

    $dao = new Dao('小白',5);
    $cat = new Cat('小花',2);
    $dao -> eat();
    $cat -> eat();

可以看到在前面定義了一個數組用來存儲路徑。
注意:在函數中使用global聲明一下,才能使用全局變量。

總結

在面向對象中用到靜態屬性和靜態方法的時候還是很多的。同時權限修飾符在面向對象中是很重要的,因爲我們通過修飾符控制訪問權限。魔術方法的掌握,也可以讓我們在訪問中明白各種調理機制。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章