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
推荐阅读>>
abstract class和interface有什么区别?