PHP中的static
關於靜態變數和方法的問題也是面試中經常會出現的問題,這種問題多看手冊搞明白原委就能解決,只是確實關於靜態變數的問題還是比較繞的,這裡我們就結合手冊用實際的程式碼來看!
class Test
{
static $v = 'a';
static function showV()
{
echo self::$v;
}
function showVV()
{
echo self::$v;
}
static function showVVV()
{
// $this->showVV(); // 會直接報錯
}
}
先準備一個類,這裡面有靜態變數、靜態方法,其中showV()方法是靜態方法呼叫靜態變數,showVV()方法是普通方法呼叫靜態變數,showVVV()方法是普通方法呼叫靜態方法。
從註釋中可以看出第一個問題,普通方法使用KaTeX parse error: Expected 'EOF', got '調' at position 5: this調̲用靜態方法會報錯,也就是說,this這個東東對於一切靜態的東西都是不友好的,不信您開啟註釋試試,也可以去呼叫靜態的$v變數,直接就是語法錯誤的提示。
接下來,我們例項化類,並開始一些測試
$t = new Test();
$t->showV();
//echo $t->v; // 報異常
echo Test::$v;
//Test::showVV(); // 報異常
$t->showVV();
- 1行:例項化的類直接呼叫showV(),這是沒問題的,靜態方法可以透過普通的方式呼叫,當然我們正規的應該是使用Test::showV()來進行呼叫,注意這裡面試的時候會是坑
- 2行:正常呼叫
- 2行:直接->v是不行的,方法可以進行普通呼叫,但屬性不行
- 3行:用靜態呼叫的方式是沒問題的
- 4行:正常獲取靜態變數
- 5行: 使用::當然不能呼叫非靜態方法啦
- 6行:正常方法中可以使用靜態變數
那麼問題來了,靜態方法中不能使用$this,如何獲得變數內容呢?請參考單例模式,將來我們會在設計模式的系列文章中講到,這裡先賣個關子,大家也可以自己研究下。
上面是正常來說一些比較簡單的靜態屬性和方法的演示,接下來好玩的東西就來了。
> 初始化特性
class Calculate
{
function cacl()
{
static $a = 1;
echo $a;
$a++;
}
static function cacl2()
{
static $a = 1;
echo $a;
$a++;
}
static $b = 1;
static function cacl3()
{
echo self::$b;
self::$b++;
}
}
$calculate = new Calculate();
$calculate->cacl(); // 1
$calculate->cacl(); // 2
Calculate::cacl2(); // 1
Calculate::cacl2(); // 2
Calculate::cacl3(); // 1
Calculate::cacl3(); // 2
看著程式碼很多,其實都是在講一件事兒,如果是普通的KaTeX parse error: Expected 'EOF', got '和' at position 2: a和̲b,那麼每次都在重新賦值,echo出來的都是0,但是靜態屬性可不一樣。靜態屬性是執行時計算的,只在第一次賦值的時候是真正的賦值操作,而後並不會進行賦值,可以相當於這一行程式碼不存在。
**靜態變數只在區域性的作用域中存在,離開這個作用域也不會丟失,當然也不能再次初始化。**學過前端的同學一定會拍案而起,這不是閉包的作用域嘛??確實很像,而且用處也非常像,比如我們做一個遞迴:
function test1()
{
static $count = 0;
$count++;
echo $count;
if ($count < 10) {
test();
}
$count--;
}
test1();
在不瞭解static之前,結束遞迴我們可能需要給方法傳遞一個數字進來,但現在似乎是不需要了,使用內部的靜態變數就可以解決了。
> 引用物件問題
class Foo
{
public $a = 1;
}
function getRefObj($o)
{
static $obj;
var_dump($obj);
if (!isset($obj)) {
$obj = &$o;
}
$obj->a++;
return $obj;
}
function getNoRefObj($o)
{
static $obj;
var_dump($obj);
if (!isset($obj)) {
$obj = $o;
}
$obj->a++;
return $obj;
}
$o = new Foo;
$obj1 = getRefObj($o); // NULL
$obj2 = getRefObj($o); // NULL
$obj3 = getNoRefObj($o); // NULL
$obj4 = getNoRefObj($o); // Foo
又是一大串程式碼,啥也不說,先複製下來執行一下看看結果是不是一樣。在使用引用物件時,我們賦值的是記憶體引用地址。但是同樣的原因,靜態屬性是執行時產生的,而引用地址不是靜態地儲存,於是,賦不上值了唄,永遠會是NULL。不信你接著用getRefObj()再生成幾個試試。實際應用中反正要記住,這種情況下千萬不要把引用值賦給靜態變數就行了,而上面原因的理解確實還是比較繞的,能講明白最好,講不明白就記住這個事兒。
> 後期靜態繫結
class A
{
static function who()
{
echo __CLASS__ . "n";
}
static function test()
{
self::who();
}
}
class B extends A
{
static function who()
{
echo __CLASS__ . "n";
}
}
B::test(); // A
先看這一段,使用self輸出的結果會是A,但如果使用普通的類例項化,並且使用普通方法的話,輸出的會是B,大家可以嘗試下。原因呢,就是self是取決於當前定義方法所在的類。這就是靜態屬性方法的另一大特點,不例項化,跟隨著類而不是例項。
class A{…},這個東西叫做類,是對現實的抽象,我們可以理解為一個模板,這裡面的東西是假的,沒有生命的。KaTeX parse error: Expected 'EOF', got '了' at position 10: a = new A了̲之後,這個a才是物件,相當於是複製一了個模板做了一個真的東西出來,是有生命的。就好像我們做一個錘子,需要一個模具,這玩意就是類,然後澆鑄金屬後成型拿出來,這玩意就是物件。一個物件有真正的記憶體地址空間的。
非靜態的屬性和方法是在物件中的,是我們澆進去的金屬。也就是new了之後才有的東西,而靜態屬性和方法是依附於class A的,是執行時進行編譯讀取的。
現在我們回過頭來看最早的例子,普通方法中呼叫靜態方法或變數,實際上就是在這個例項化物件中呼叫了Test::showV(),只是我們使用了self關鍵字而已。依然是走的靜態過程而不是這個物件中真的包含了showV()這個方法,因此,$this當然取不到啦!
那麼,如何讓父類A中test()方法去呼叫到子類的who()方法呢?
class AA
{
static function who()
{
echo __CLASS__ . "n";
}
static function test()
{
static::who();
}
}
class BB extends AA
{
static function who()
{
echo __CLASS__ . "n";
}
}
BB::test(); // BB
沒錯,使用static::關鍵字這種形式呼叫,static表示執行最初時的類,不是方法定義時的類。這樣就完成了後期靜態繫結。另外,parent::和self::是會轉發這個鏈條的。
class AAA
{
public static function foo()
{
static::who();
}
public static function who()
{
echo __CLASS__ . "n";
}
}
class BBB extends AAA
{
public static function test()
{
AAA::foo();
parent::foo();
self::foo();
}
public static function who()
{
echo __CLASS__ . "n";
}
}
class CCC extends BBB
{
public static function who()
{
echo __CLASS__ . "n";
}
}
CCC::test(); // AAA、CCC、CCC
- CCC繼承了BBB,BBB繼承了AAA
- 在AAA中的foo()方法使用了static::who()來呼叫who()方法
- BBB中的test()執行了三種呼叫
- 結果是parent::foo()和self::foo()都將CCC傳遞了過去,最後使用的是CCC的who()方法
這個例子看著很繞,但其實結論就一個,如果父類使用了static關鍵字來呼叫父子類都有的內容,那麼就是以哪個子類在外面進行呼叫了為準,就像普通類的方法呼叫 一樣。反過來,self就是以這個self關鍵字所在的類為準。
說了這麼多,也算是把static靜態的特性講解的差不多了。在實際應用中還是要綜合考慮,不能因為靜態屬性方便就全都使用靜態屬性和方法或者完全不使用,還是要結合各路業務需求進行取捨。
具體程式碼:
github.com/zhangyue0503/php/blob/master/newblog/php-static.php
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/1343/viewspace-2826644/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- PHP中static與yield關鍵字的思考PHP
- PHP 中 bind 的用法 self 和 static 的區別PHP
- php static dynamicPHP
- static變數在php和java中的生命週期變數PHPJava
- php class中public,private,protected,static的區別,以及例項PHP
- PHP中new static()與new self()的區別異同分析PHP
- PHP 中 static 靜態屬性和靜態方法的呼叫PHP
- PHP類的靜態(static)方法和靜態(static)變數PHP變數
- Java中static、final、static final的區別Java
- PHP new self()和new static()的區別PHP
- Java中static的含義Java
- java中的Static、final、Static final各種用法Java
- C和C++中的staticC++
- java中的static關鍵字Java
- Java中的static詳細講解Java
- java中static關鍵字的作用Java
- Java中的static關鍵字解析Java
- PHP 的 new static 和 new self 具體有什麼?PHP
- java中static作用詳解Java
- static 資料 程式設計中的作用程式設計
- PHP延遲靜態繫結:static關鍵字PHP
- C++中static關鍵字C++
- java中的static和final關鍵字Java
- C/C++中Static和Const的作用C++
- iOS中的static const extern關鍵字iOS
- C++中的static成員、static const成員和const成員的初始化C++
- C語言中的 static變數、static函式C語言變數函式
- Java中final與static final的區別Java
- Java中static、final用法小結Java
- static方法在繼承中如何使用?繼承
- 筆試題 類中static變數筆試變數
- PHP static 關鍵字和 self 關鍵字例項化的區別PHP
- static
- JAVA面試題 淺析Java中的static關鍵字Java面試題
- C中的auto、static、register、extern、const和volitate
- php 中的常量PHP
- PHP中的scalarPHP
- Java中可以宣告一個類為Static嗎?Java