聲明靜態的類變量和方法可以不需要實例化類對象的情況下對他們進行調用。靜態類不能被類對象調用(類的靜態方法可以)。
靜態成員不能被類的對象通過箭頭符號->來調用。靜態的調用一個非靜態方法會導致一個E_STRICT級別的警告。
class Foo {
public static function aStaticMethod() {
// ...
}
}
Foo::aStaticMethod(); //調用靜態方法
二、靜態成員例:
class Foo {
public static $my_static = 'foo';
public function staticValue() {
return self::$my_static;
}
}
class Bar extends Foo
{
public function fooStatic() {
return parent::$my_static;
}
}
Foo::$my_static . " n";
$foo = new Foo();
print $foo->staticValue() . " n";
print $foo->my_static . " n"; // 未定義的"Property" my_static
// $foo::my_static is not possible
print Bar::$my_static . " n";
$bar = new Bar();
print $bar->fooStatic() . " n";
PHP5的static的實現上和其他的OO語言有很大的不同。
先看一部分代碼
class A{
protected static $v='A';
public static function getV(){
return self::$v;
}
public static function setV($v){
self::$v=$v;
}
}
class B extends A{
protected static $v='B';
}
class C extends B{
protected static $v='C';
}
echo 'A::getV '.A::getV();
echo 'B::getV '.B::getV();
echo 'C::getV '.C::getV();
按通常的理解,應該是:
A::getV A
B::getV B
C::getV C
但輸出的結果是:
A::getV A
B::getV A
C::getV A
再做個實驗:
class A{
protected static $v='A';
public static function getV(){
return self::$v;
}
public static function setV($v){
self::$v=$v;
}
}
class B extends A{
protected static $v='B';
public static function getV(){
return self::$v;
}
}
class C extends B{
protected static $v='C';
}
echo 'A::getV '.A::getV();
echo 'B::getV '.B::getV();
echo 'C::getV '.C::getV();
復制代碼
猜猜結果是什麼?
A::getV A
B::getV B
C::getV B
最後一個例子:
class A{
protected static $v='A';
public static function getV(){
return self::$v;
}
public static function setV($v){
self::$v=$v;
}
}
class B extends A{
protected static $v='B';
public static function getV(){
return self::$v;
}
public static function setV($v){
self::$v=$v;
}
}
class C extends B {
protected static $v='C';
}
A::setV('C');
B::setV('D');
C::setV('E');
echo 'A::getV '.A::getV();
echo 'B::getV '.B::getV();
echo 'C::getV '.C::getV();
復制代碼
輸出結果:
A::getV C
B::getV E
C::getV E
ok
看到這裡大家都應該清楚了,PHP5的static的實現比較“特別”,從手冊裡面也能看到說明。調用static方法是編譯時確定的,而不是按照調用時的繼承關系來確定。簡單的說,當調用static方法時,其起始的范圍是實現這個static方法(包括重載)的類范圍,而不是按照調用的繼承關系的類。比如調B::getV(),如果B類中定義或者重載了getV,那麼此時確定的當前類就是B,否則向上追溯到其父類假設是A,如果A扔沒有,繼續向上查找,一旦找到,那麼當前類就是這個定義getV的類。此時self指向的就是這個確定的類,而不是起始調用getMethod的那個class,因此,如果此時這個類假設為A沒有定義static $v,則會引發一個錯誤。
PHP5的這種方式導致了很多困惑,尤其希望實現static重載的時候,你無法通過在父類定義通用的方法,而試圖通過子類重載一些static field來實現static級別的繼承。我在用PHP5實現Active Record模式的時候遇到了這些問題,最後只能通過一些hack的手法來實現。不知道PHP6是否能夠改變這種方式,希望如此。
靜態的成員, 所針對的是類而不是對象。在訪問類的成員時, 首先要找當前類有沒有這個成員, 然後再找父類的.。就如上面的那些代碼:
第一個例子的。 B::getV()和C::getV()中的getV()成員函數。 都是在A中定義的。, 所以對於getV()來說。 當前類就是A。 就首先找A類中的$v; 。
第二個例子的。 A::getV()就不用說了。B::getV()和C::getV()中的getV()成員函數,都是在B中定義的,,所以對於getV()來說,,當前類就是B, 就首先找B類中的$v; 。
第三個例子的, 跟上一個例子差不多,主要是在setV()裡面的,B::setV('D')和C::setV('E')中的setV()都是B類的, 當然$v也就是B類的,B::setV('D')使B::$v='D', C::setV('E')使B::$v='E',這時調用getV()時,當前就都是E了。
在C++語言中有一個域的訪問規則,就是這樣的, 我平時用其它語言寫程序一般也是遵循這個規則的,所以其它語言沒有認真研究過這方面的。
四、需要注意的是,在函數裡面使用global、static時遇new stdclass引用的情況,這時&new stdclass將會失效,應避免使用引用,直接用new stdclass。