php中static 靜態關鍵字


一直依賴對於php中static關鍵字比較模糊,只是在單例模式中用過幾次。上網查了查,沒有找到很全的介紹,自己總結一下。

根據使用位置分為兩部分 1、函數體中的靜態變量 2、類中的靜態屬性和方法

1 函數體中的靜態變量

static $a = 1;
function num1(){
    static $a = 0;
    return $a++;
}

function num2(){
    $a = 5555;
    global $a;
    echo $a++;
}

function num3(){
    static $a = 100;
    return $a++;
}

echo num1();    //0

num2();       //1
echo $a;       //2 

echo num3();    //100
echo num3();    //101
echo num3();    //102

 

從中我們看出幾點

1 函數體中的靜態變量與全局中的靜態變量不沖突,只有在關鍵字global作用下才會使局部與全局統一

2 函數體中的靜態變量在函數調用的時候只會被初始化一次

因為靜態變量在全局數據區分配內存;此處要提一下 緩存在邏輯上大致區分為 棧 堆 全局區  程序代碼區 文字常量區

棧 存放變量名(會有一部分的整型,浮點型,字符串存放在此處)

堆 存放變量值

全局區 存放靜態變量,全局變量

程序代碼區 存放要執行的函數及方法

文字常量區 存放常量等

大致是上面的樣子,相當相當不嚴格,但是可以很好的幫助我們記憶數據間的關系。

 

2 類中的靜態成員

類中的靜態成員包含靜態屬性和靜態方法

 class first{    public $num = 1;

//聲明一個靜態屬性 static $name = '456'; //聲明一個靜態方法 public static function self_use(){      echo self::$name;
echo self::out(); } public static function static_use(){ echo static::out(); } public static function method(){ echo 'first'; } //這是一個錯誤的調用 public function my_wrong(){ //echo self::dis(); } public function dis(){ echo '只是做一個展示'; } } class second extends first { public static function method(){ echo 'second'; } } $one = new first(); $one::$name; $one->self_use();
$one->static_use();


$two = new second();
$two->self_use();
$two->static_use();

關於類中的靜態方法我們從兩個角度介紹 1 使用角度 2 理論角度

1 在實際使用中我們需要注意幾點

a. 靜態方法可以調用靜態屬性,禁止調用非靜態屬性。

b. 靜態方法內不允許出現$this,出現就停掉腳本。

c. 在靜態方法中調用類中其他靜態方法有兩種格式 self:: 和 static 。根據輸出結果我們了解到兩種方式的區別是 使用 self::method() 調用本方法所在類的 method() 方法。使用static::method(),會調用整個繼承家族最后定義的方法。

這個感覺很繞繞啊,形象點理解就是在父類中聲明一個靜態方法A,子類中重寫了這個靜態方法A。在父類其他方法中使用self調用A,會執行父類中A的方法;但是使用static調用,就會執行子類A的方法;

2 從理論角度介紹

早期的結構化編程幾乎都是靜態方法,出現面向對象之后,才有了實例化方法的感念。而面向對象並不能完全解決掉靜態方法使用的問題。我們可以這樣理解,靜態方法和實例化方法雖然都是類的,但是二者的調用方式是不同的,靜態方法和屬性在類在使用class::就會被存進內存,然后使用。但是實例化方法和非靜態屬性,需要實例化 new之后才會被存進內存使用。這就解釋了a 中靜態方法不能調用非靜態屬性,你靜態方法喊一嗓子就出來了,我非靜態屬性不行啊,沒批准(new)我出不來啊;而b中的$this是面向對象出來之后的產物專門用於實例化對象時候調用對象中的屬性和方法用的,是對象的專屬技能,這個技能靜態方法不能使用。

總結此處為看了之后總結的,我木有那么高水平:

大家對這個問題都有一個共識:那就是實例化方法更多被使用和穩妥,靜態方法少使用。有時候我們對靜態方法和實例化方法會有一些誤解。

1、大家都以為“ 靜態方法常駐內存,實例方法不是,所以靜態方法效率高但占內存。

事實上,他們都是一樣的,在加載時機和占用內存上,靜態方法和實例方法是一樣的,在類型第一次被使用時加載。調用的速度基本上沒有差別。

 

2、大家都以為“ 靜態方法在堆上分配內存,實例方法在堆棧上

事實上所有的方法都不可能在堆或者堆棧上分配內存,方法作為代碼是被加載到特殊的代碼內存區域,這個內存區域是不可寫的。

方法占不占用更多內存,和它是不是static沒什么關系。   
  因為字段是用來存儲每個實例對象的信息的,所以字段會占有內存,並且因為每個實例對象的狀態都不一致(至少不能認為它們是一致的),所以每個實例對象的所有字段都會在內存中有一分拷貝,也因為這樣你才能用它們來區分你現在操作的是哪個對象。   
  但方法不一樣,不論有多少個實例對象,它的方法的代碼都是一樣的,所以只要有一份代碼就夠了。因此無論是static還是non-static的方法,都只存在一份代碼,也就是只占用一份內存空間。   
  同樣的代碼,為什么運行起來表現卻不一樣?這就依賴於方法所用的數據了。主要有兩種數據來源,一種就是通過方法的參數傳進來,另一種就是使用class的成員變量的值……

 

3、大家都以為“實例方法需要先創建實例才可以調用,比較麻煩,靜態方法不用,比較簡單

事實上如果一個方法與他所在類的實例對象無關,那么它就應該是靜態的,而不應該把它寫成實例方法。所以所有的實例方法都與實例有關,既然與實例有關,那么創建實例就是必然的步驟,沒有麻煩簡單一說。

當然你完全可以把所有的實例方法都寫成靜態的,將實例作為參數傳入即可,一般情況下可能不會出什么問題。

從面向對象的角度上來說,在抉擇使用實例化方法或靜態方法時,應該根據是否該方法和實例化對象具有邏輯上的相關性,如果是就應該使用實例化對象  反之使用靜態方法。這只是從面向對象角度上來說的。

如果從線程安全、性能、兼容性上來看  也是選用實例化方法為宜。

 

我們為什么要把方法區分為:靜態方法和實例化方法 ?

如果我們繼續深入研究的話,就要脫離技術談理論了。早期的結構化編程,幾乎所有的方法都是“靜態方法”,引入實例化方法概念是面向對象概念出現以后的事情了,區分靜態方法和實例化方法不能單單從性能上去理解,創建c++,java,c#這樣面向對象語言的大師引入實例化方法一定不是要解決什么性能、內存的問題,而是為了讓開發更加模式化、面向對象化。這樣說的話,靜態方法和實例化方式的區分是為了解決模式的問題。

拿別人一個例子說事:

 比如說“人”這個類,每個人都有姓名、年齡、性別、身高等,這些屬性就應該是非靜態的,因為每個人都的這些屬性都不相同;但人在生物學上屬於哪個門哪個綱哪個目等,這個屬性是屬於整個人類,所以就應該是靜態的——它不依賴與某個特定的人,不會有某個人是“脊椎動物門哺乳動物綱靈長目”而某個人卻是“偶蹄目”的。

 

 參考文獻:

PHP中靜態方法和實例化方法的區別

static關鍵字及變量存儲位置總結 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM