1.Abstract class(抽象類)
- 抽象類是指在 class 前加了 abstract 關鍵字且存在抽象方法(在類方法 function 關鍵字前加了 abstract 關鍵字)的類。
- 抽象類不能被直接實例化。抽象類中只定義(或部分實現)子類需要的方法。子類可以通過繼承抽象類並通過實現抽象類中的所有抽象方法,使抽象類具體化。
- 如果子類需要實例化,前提是它實現了抽象類中的所有抽象方法。如果子類沒有全部實現抽象類中的所有抽象方法,那么該子類也是一個抽象類,必須在 class 前面加上 abstract 關鍵字,並且不能被實例化。
舉例說明
abstract Class AbstractClass { abstract public function A();//這里不要攜帶body abstract protected function B();//這里不要攜帶body public function C(){}//這里要攜帶body } class ClassA extends AbstractClass { public function A(){ echo "Hello,I'm A <br/>"; } protected function B(){ echo "Hello,I'm B <br/>"; } public function E() { echo "Hello,I'm E <br/>"; } }
注意要點:
- 如果 AbstractClass實現了抽象方法 B() ,那么 ClassA 中 B() 方法的訪問控制不能比 AbstractClass 中 B() 的訪問控制更嚴格,也就是說:
1)、如果AbstractClass中B為Public,則ClassA中B只能為public
2)、如果AbstractClass中B為Protected,則ClassA中B只能為public或Protected
3)、注意,AbstractClass的抽象方法不能為Private
- 抽象方法與普通的方法不一樣,它只是子類中普通方法的一個占位符(只是占個地主不啟作用),沒有任何代碼,也沒有"{}"包含,而是以";"結束的.
2.Interface(接口)
- 接口用關鍵字 interface 來聲明。抽象類提供了具體實現的標准,而接口則是純粹的模版。接口只定義功能,而不包含實現的內容。
- interface 是完全抽象的,只能聲明方法,而且只能聲明 public 的方法,不能聲明 private 及 protected 的方法,不能定義方法體,也不能聲明實例變量 。然而, interface 卻可以聲明常量變量 。但將常量變量放在 interface 中違背了其作為接口的作用而存在的宗旨,也混淆了 interface 與類的不同價值。如果的確需要,可以將其放在相應的 abstract class 或 Class 中。
- 任何實現接口的類都要實現接口中所定義的所有方法,否則該類必須聲明為 abstract 。
- 一個類可以在聲明中使用 implements 關鍵字來實現某個接口。這么做之后,實現接口的具體過程和繼承一個僅包含抽象方法的抽象類是一樣的。一個類可以同時繼承一個父類和實現任意多個接口。 extends 子句應該在 implements 子句之前。 PHP 只支持繼承自一個父類,因此 extends 關鍵字后只能跟一個類名。
- 接口不可以實現另一個接口,但可以繼承多個
實例:
interface A { public function fA(); Public function fB(); } interface B { public function fC(); Public function fD(); } interface C extends A,B { } class M implements C { public function fA(){ } public function fB(){ } public function fC(){ } public function fD(){ } }
3.Abstract Class與Interface的異同
相同點
- 兩者都是抽象類,都不能實例化。
- interface 實現類及 abstract class 的子類都必須要實現已經聲明的抽象方法。
不同點
- interface 需要實現,要用 implements ,而 abstract class 需要繼承,要用 extends 。
- 一個類可以實現多個 interface ,但一個類只能繼承一個 abstract class 。
- interface 強調特定功能的實現,而 abstract class 強調所屬關系。
- 盡管 interface 實現類及 abstract class 的子類都必須要實現相應的抽象方法,但實現的形式不同。 interface 中的每一個方法都是抽象方法,都只是聲明的 (declaration, 沒有方法體 ) ,實現類必須要實現。
而 abstract class 的子類可以有選擇地實現。這個選擇有兩點含義:
a) abstract class 中並非所有的方法都是抽象的,只有那些冠有 abstract 的方法才是抽象的,子類必須實現。那些沒有 abstract 的方法,在 abstract class 中必須定義方法體;
b) abstract class 的子類在繼承它時,對非抽象方法既可以直接繼承,也可以覆蓋;而對抽象方法,可以選擇實現,也可以留給其子類來實現,但此類必須也聲明為抽象類。既是抽象類,當然也不能實例化。
- abstract class 是 interface 與 class 的中介。 abstract class 在 interface 及 class 中起到了承上啟下的作用。一方面, abstract class 是抽象的,可以聲明抽象方法,以規范子類必須實現的功能;另一方面,它又可以定義缺省的方法體,供子類直接使用或覆蓋。另外,它還可以定義自己的實例變量,以供子類通過繼承來使用。
- 接口中的抽象方法前不用也不能加 abstract 關鍵字,默認隱式就是抽象方法,也不能加 final 關鍵字來防止抽象方法的繼承。而抽象類中抽象方法前則必須加上 abstract 表示顯示聲明為抽象方法。
- 接口中的抽象方法默認是 public 的,也只能是 public 的,不能用 private , protected 修飾符修飾。而抽象類中的抽象方法則可以用 public , protected 來修飾,但不能用 private 。
引用與推薦閱讀>> PHP中的 抽象類(abstract class)和 接口(interface)
4.Abstract關鍵字
abstract關鍵字用於定義抽象方法與抽象類。
抽象方法指:沒有方法體的方法,具體就是在方法聲明的時候沒有{},而是直接分號結束。一般用abstract定義的方法被稱為抽象方法。在interface中所有的方法都是抽象方法。
定義抽象方法形如:
abstract function func_name();
抽象類指:只要一個類里面有一個方法是抽象方法,那么這個類就定義為抽象類。抽象類同樣用abstract關鍵字來定義。關於抽象類的概念,更多了解可以看上面的解釋。
5.Final關鍵字
final關鍵字,如果父類中的方法被聲明為final,則子類無法覆蓋該方法,如果一個類被聲明為final,則不能被繼承。
6.Static關鍵字
static關鍵字用來定義靜態方法和屬性,也可以用來定義靜態變量以及后期靜態綁定。
- 靜態方法和屬性:
聲明類屬性或方法為靜態,就可以不實例化類而直接訪問。靜態屬性不能通過一個類已實例化的對象來訪問(但靜態方法可以)。在沒有指定訪問控制(public,private,protected)的時候,屬性和方法默認public。
靜態方法不需要通過對象即可調用,所以偽變量$this在靜態方法中不可用。
靜態屬性不可以由對象通過 -> 操作符來訪問。
用靜態方式調用一個非靜態方法會導致一個 E_STRICT 級別的錯誤。
就像其它所有的 PHP 靜態變量一樣,靜態屬性只能被初始化為文字或常量,不能使用表達式。所以可以把靜態屬性初始化為整數或數組,但不能初始化為另一個變量或函數返回值,也不能指向一個對象。
自 PHP 5.3.0 起,可以用一個變量來動態調用類。但該變量的值不能為關鍵字 self,parent 或 static。
代碼說明:
\Libs\Foo.php
<?php namespace Libs; class Foo { public static $my_static = 'foo'; public function staticFunc() { return self::$my_static; } }
\Libs\SonFoo.php
<?php namespace Libs; class SonFoo extends Foo { public function sonStatic() { return parent::$my_static; } }
\Libs\UseFoo.php
<?php namespace Libs; class UseFoo { static public function index() { $eof = "<br/>"; echo Foo::$my_static,1,$eof; //靜態屬性的調用方式 $foo = new Foo(); echo $foo->staticFunc(),2,$eof; echo $foo->my_static,3,$eof;//報錯,因為不能使用->方式調用靜態屬性 echo $foo::$my_static,4,$eof; $newfoo = 'Libs\Foo'; echo $newfoo::$my_static,5,$eof;// As of PHP 5.3.0 echo SonFoo::$my_static,6,$eof; $sonfoo = new SonFoo(); echo $sonfoo->sonStatic(),7,$eof; } }
調用
UseFoo::index();
結果:
foo1
foo2
| ( ! ) Strict standards: Accessing static property Libs\Foo::$my_static as non static in ......Libs\UseFoo.php on line 15 | ||||
|---|---|---|---|---|
| Call Stack | ||||
| # | Time | Memory | Function | Location |
| 1 | 0.0008 | 244064 | {main}( ) | ..\index.php:0 |
| 2 | 0.0026 | 262624 | Libs\UseFoo::index( ) | ..\index.php:51 |
| ( ! ) Notice: Undefined property: Libs\Foo::$my_static in ......Libs\UseFoo.php on line15 | ||||
|---|---|---|---|---|
| Call Stack | ||||
| # | Time | Memory | Function | Location |
| 1 | 0.0008 | 244064 | {main}( ) | ..\index.php:0 |
| 2 | 0.0026 | 262624 | Libs\UseFoo::index( ) | ..\index.php:51 |
3
foo4
foo5
foo6
foo7
- 使用靜態變量:
變量范圍的另一個重要特性是靜態變量(static variable)。靜態變量僅在局部函數域中存在,但當程序執行離開此作用域時,其值並不丟失。看看下面的例子:
<?php function test($id) { static $count=0; echo "{$id}=>count is :".$count."<br/>"; $count++; } test(1); test(2); test(3);
結果:
1=>count is :0
2=>count is :1
3=>count is :2
靜態變量也提供了處理遞歸函數(遞歸函數是一種調用自己的函數)的方法。下面列出一個使用static變量完成遞歸的函數。
舉個小例子
function test($end) { static $sum=0; if($end>0){ $sum +=$end; $end--; test($end); } return $sum; } echo test(10);//輸出55
注意注意注意!上面代碼只是一個小例子,實際生產中千萬不要這么來求和!!!
- 后期靜態綁定:
自 PHP 5.3.0 起,PHP 增加了一個叫做后期靜態綁定的功能,用於在繼承范圍內引用靜態調用的類。
准確說,后期靜態綁定工作原理是存儲了在上一個“非轉發調用”(non-forwarding call)的類名。當進行靜態方法調用時,該類名即為明確指定的那個(通常在 :: 運算符左側部分);當進行非靜態方法調用時,即為該對象所屬的類。所謂的“轉發調用”(forwarding call)指的是通過以下幾種方式進行的靜態調用:self::,parent::,static:: 以及 forward_static_call()。可用 get_called_class() 函數來得到被調用的方法所在的類名,static:: 則指出了其范圍。
該功能從語言內部角度考慮被命名為“后期靜態綁定”。“后期綁定”的意思是說,static:: 不再被解析為定義當前方法所在的類,而是在實際運行時計算的。也可以稱之為“靜態綁定”,因為它可以用於(但不限於)靜態方法的調用。
1).self:: 使用self::或者__CLASS__對之前類的靜態引用,取決於定義當前方法所在的類
class A { public static function who() { echo __CLASS__; } public static function test() { self::who(); } } class B extends A { public static function who() { echo __CLASS__; } } B::test();//輸出A
2).static:: 后期靜態綁定本想通過引入一個新的關鍵字表示運行時最初調用的類來繞過限制。簡單地說,這個關鍵字能夠讓你在上述例子中調用 test() 時引用的類是 B 而不是 A。最終決定不引入新的關鍵字,而是使用已經預留的 static 關鍵字。
//簡單用法
class A { public static function who() { echo __CLASS__; } public static function test() { static::who(); // 后期靜態綁定從這里開始 } } class B extends A { public static function who() { echo __CLASS__; } } B::test();//輸出B
在非靜態環境下,所調用的類即為該對象實例所屬的類,由於$this-> 會在同一作用范圍內嘗試調用私有方法,而static::則可能給出不同結果。static::只能用於靜態屬性
<?php class A { private function foo() { echo "success!\n"; } public function test() { $this->foo(); static::foo(); } } class B extends A { /* foo() will be copied to B, hence its scope will still be A and * the call be successful */ } class C extends A { private function foo() { /* original method is replaced; the scope of the new one is C */ } } $b = new B(); $b->test(); $c = new C(); $c->test(); //fails ?>
success!
success!
success!
Fatal error: Call to private method C::foo() from context 'A' in /tmp/test.php on line 9
后期靜態綁定的解析會一直到取得一個完全解析了的靜態調用為止。另一方面,如果靜態調用使用 parent:: 或者 self:: 將轉發調用信息。
class A { public static function foo() { static::who(); } public static function who() { echo __CLASS__."\n"; } } class B extends A { public static function test() { A::foo(); parent::foo(); self::foo(); } public static function who() { echo __CLASS__."\n"; } } class C extends B { public static function who() { echo __CLASS__."\n"; } } C::test();//輸出結果為ACC
推薦閱讀>>
