[关闭]
@Chiang 2019-12-24T10:30:19.000000Z 字数 9691 阅读 561

static关键字 self 后期静态绑定 抽象类 接口 final关键字

PHP


static

  1. 1.声明方法: 如果没有修饰符修饰默认为public
  2. class Person
  3. {
  4. //声明静态属性
  5. public static $name = 'foo';
  6. //声明静态方法
  7. public static function say() {
  8. return self::$name;
  9. }
  10. }
  11. $person = new Person();
  12. 2.使用方法:
  13. 访问静态属性
  14. 类中:Person::$name; self::$name
  15. 类外:1.$me = new Person(); echo $me::$name;
  16. 2.Person::$name;
  17. 访问静态方法
  18. 类中:Person::say(); self::say();
  19. 类外:1.$me = new Person(); $me::say();
  20. 2.$me = new Person(); $me->say();
  21. /*************/
  22. class foo {
  23. function bar() {
  24. echo 'I am not static!';
  25. }
  26. }
  27. foo::bar();//php7说静态调用非静态方法将被弃用
  28. 3.实战:
  29. 1)初始化一个值,不能为含有变量和函数方法:
  30. public static $writeToBodyOfCompany = array('1190','1370','1380','1930');
  31. 以下两种为错误定义:
  32. public static $number1 = self::$age; #错的
  33. public static $number2 = mt_rand(18,24); #错的
  34. 2)方法中变量设为static:
  35. static的定义语句只会被执行一次,但是它的值会被函数记住,直到程序终止
  36. function increment1(){
  37. //static这个语句只会在该程序运行的第一次调用生效,但是这个值在函数执行结束后会被该函数记住,
  38. //也就是说下次执行到这个函数的,$a是已经存在的且值为上次运行后的值
  39. static $n = 0;
  40. $n++;
  41. echo $n;
  42. }
  43. increment1(); #1
  44. increment1(); #2
  45. increment1(); #3
  46. function increment2(){
  47. $n = 0;
  48. $n++;
  49. echo $n;
  50. }
  51. increment2(); #1
  52. increment2(); #1
  53. increment2(); #1
  54. 4.好处:
  55. 1)不需要实例化类就可以访问属性和方法
  56. 2)在内存中只有一份,为所有的实例共用,而实例化的方式会创建多个内存
  57. 3)访问速度比实例化访问快
  58. 4)静态方法中不能访问非静态属性和方法(还未创建,速度快的原因)
  59. 5)静态方法的缺点是不自动进行回收,而实例化的则可以做销毁

PHP static

  1. <?php
  2. class Person{
  3. public static $name = 'Voyager';
  4. public static function say(){
  5. echo 'My name is ' . self::$name;
  6. }
  7. }

一.介绍

  • 不需要实例化类可直接访问。
  • 在内存中只有一份,为所有的实例共用。
  • 访问速度比实例化访问快。
  • 静态方法中不能访问非静态属性和方法(还未创建,速度快的原因)。
  • 静态属性不能通过实例化的对象方式访问,但静态方法可以。
  • 不能直接定义为变量或者方法,详见三。
  • 定义后不被自动回收,详见四。

二.访问方式

  1. 访问静态属性
  2. 1.Person::$name;
  3. 2.$me = new Person();
  4. echo $me::$name;
  5. 访问静态方法
  6. 1.Person::say();
  7. 2.$me = new Person();
  8. $me::say();
  9. 3.$me = new Person();
  10. $me->say();

三.初始化为一个不定值

为什么static要一个不定值呢?例如一个方法处理一批订单,需要一个随机数,或者当天开始时间戳。我们需要初始化一次,然后对这批订单处理都使用这一个值,但是下一批又不一样了。

  1. <?php
  2. class Person{
  3. public static $age = 2018-1994;#正确
  4. public static $number1 = self::$age; #错的
  5. public static $number2 = mt_rand(18,24); #错的
  6. public static $number3;
  7. #正确
  8. public static function init(){
  9. if(!self::$number3){
  10. self::$number3 = mt_rand(18,24);
  11. }
  12. }
  13. #正确
  14. public function __construct(){
  15. if(!self::$number3){
  16. self::$number3 = mt_rand(18,24);
  17. }
  18. }
  19. }
  20. // init
  21. Person::init();
  22. echo Person::$number3;
  23. // __construct
  24. $a = new Person();
  25. $b = new Person();
  26. echo $a::$number3;
  27. echo '<br />';
  28. echo $b::$number3;

四.方法中的static

  1. <?php
  2. function increment1(){
  3. static $n = 0;
  4. $n++;
  5. echo $n;
  6. }
  7. increment1(); #1
  8. increment1(); #2
  9. increment1(); #3
  10. //输出‘123’
  11. function increment2(){
  12. $n = 0;
  13. $n++;
  14. echo $n;
  15. }
  16. increment2(); #1
  17. increment2(); #1
  18. increment2(); #1
  19. //输出‘111’

self 关键字

  • self__CLASS__都是对当前类的静态引用,取决于定义当前方法所在的类。也就是说self写在哪个类里面,它就引用谁。

  • $this 指向的是实际调用时的对象,也就是说,实际运行过程中,谁调用了类的属性或方法,$this 指向的就是哪个对象。但 $this 不能访问类的静态属性和常量,且 $this 不能存在于静态方法中

  • self 只是表示当前的类,所以这就是self的限制

php CLASS、get_class()与get_called_class()的区别

  • __CLASS__ 获取当前的类名,
  • get_class() 与上面一样,都是获取当前的类名
  • get_called_class() 获取当前主调类的类名

当涉及到继承时,在方法中使用类名。直接贴图了

pic_process
pic_result

MVC框架中,涉及到单例时很好用,一般在基类中

  1. public static function getInstance() {
  2. $class_name = get_called_class();
  3. if (isset(self::$instance[$class_name])) {
  4. return self::$instance[$class_name];
  5. }
  6. self::$instance[$class_name] = new $class_name;
  7. return self::$instance[$class_name];
  8. }

其他类只要继承这个类,然后通过getInstance()就实现了单例模式

后期静态绑定

自 PHP 5.3.0 起,PHP 增加了一个叫做后期静态绑定的功能,用于在继承范围内引用静态调用的类。

准确说,后期静态绑定工作原理是存储了在上一个“非转发调用”(non-forwarding call)的类名。当进行静态方法调用时,该类名即为明确指定的那个(通常在 :: 运算符左侧部分);当进行非静态方法调用时,即为该对象所属的类。所谓的“转发调用”(forwarding call)指的是通过以下几种方式进行的静态调用:self::,parent::,static:: 以及 forward_static_call()。可用 get_called_class() 函数来得到被调用的方法所在的类名,static:: 则指出了其范围。

该功能从语言内部角度考虑被命名为“后期静态绑定”。“后期绑定”的意思是说,static:: 不再被解析为定义当前方法所在的类,而是在实际运行时计算的。也可以称之为“静态绑定”,因为它可以用于(但不限于)静态方法的调用。

self:: 的限制

使用 self:: 或者 __CLASS__ 对当前类的静态引用,取决于定义当前方法所在的类

  1. <?php
  2. class A {
  3. public static function who() {
  4. echo __CLASS__;
  5. }
  6. public static function test() {
  7. self::who();
  8. }
  9. }
  10. class B extends A {
  11. public static function who() {
  12. echo __CLASS__;
  13. }
  14. }
  15. B::test();
  16. // A

后期静态绑定的用法

后期静态绑定本想通过引入一个新的关键字表示运行时最初调用的类来绕过限制。简单地说,这个关键字能够让你在上述例子中调用 test() 时引用的类是 B 而不是 A。最终决定不引入新的关键字,而是使用已经预留的 static 关键字。

  1. <?php
  2. class A {
  3. public static function who() {
  4. echo __CLASS__;
  5. }
  6. public static function test() {
  7. static::who(); // 后期静态绑定从这里开始
  8. }
  9. }
  10. class B extends A {
  11. public static function who() {
  12. echo __CLASS__;
  13. }
  14. }
  15. B::test();
  16. // B

在非静态环境下,所调用的类即为该对象实例所属的类。由于 $this-> 会在同一作用范围内尝试调用私有方法,而 static:: 则可能给出不同结果。另一个区别是 static:: 只能用于静态属性。

  1. <?php
  2. class A {
  3. private function foo() {
  4. echo "success!\n";
  5. }
  6. public function test() {
  7. $this->foo();
  8. static::foo();
  9. }
  10. }
  11. class B extends A {
  12. /* foo() will be copied to B, hence its scope will still be A and
  13. * the call be successful */
  14. }
  15. class C extends A {
  16. private function foo() {
  17. /* original method is replaced; the scope of the new one is C */
  18. }
  19. }
  20. $b = new B();
  21. $b->test();
  22. $c = new C();
  23. $c->test(); //fails
  24. // success!
  25. // success!
  26. // success!
  27. // Fatal error: Call to private method C::foo() from context 'A' in /tmp/test.php on line 9

后期静态绑定的解析会一直到取得一个完全解析了的静态调用为止。另一方面,如果静态调用使用 parent:: 或者 self:: 将转发调用信息。

  1. <?php
  2. class A {
  3. public static function foo() {
  4. static::who();
  5. }
  6. public static function who() {
  7. echo __CLASS__."\n";
  8. }
  9. }
  10. class B extends A {
  11. public static function test() {
  12. A::foo();
  13. parent::foo();
  14. self::foo();
  15. }
  16. public static function who() {
  17. echo __CLASS__."\n";
  18. }
  19. }
  20. class C extends B {
  21. public static function who() {
  22. echo __CLASS__."\n";
  23. }
  24. }
  25. C::test();
  26. // A
  27. // C
  28. // C

抽象类 abstract

抽象类 abstract 声明类中方法,不能声明属性

PHP 5 支持抽象类和抽象方法。定义为抽象的类不能被实例化。任何一个类,如果它里面至少有一个方法是被声明为抽象的,那么这个类就必须被声明为抽象的。被定义为抽象的方法只是声明了其调用方式(参数),不能定义其具体的功能实现。

继承一个抽象类的时候,子类必须定义父类中的所有抽象方法;另外,这些方法的访问控制必须和父类中一样(或者更为宽松)。例如某个抽象方法被声明为受保护的,那么子类中实现的方法就应该声明为受保护的或者公有的,而不能定义为私有的。此外方法的调用方式必须匹配,即类型和所需参数数量必须一致。例如,子类定义了一个可选参数,而父类抽象方法的声明里没有,则两者的声明并无冲突。 这也适用于 PHP 5.4 起的构造函数。在 PHP 5.4 之前的构造函数声明可以不一样的。

  1. <?php
  2. abstract class AbstractClass
  3. {
  4. // 强制要求子类定义这些方法
  5. abstract protected function getValue();
  6. abstract protected function prefixValue($prefix);
  7. // 普通方法(非抽象方法)
  8. public function printOut() {
  9. print $this->getValue() . "\n";
  10. }
  11. }
  12. class ConcreteClass1 extends AbstractClass
  13. {
  14. protected function getValue() {
  15. return "ConcreteClass1";
  16. }
  17. public function prefixValue($prefix) {
  18. return "{$prefix}ConcreteClass1";
  19. }
  20. }
  21. class ConcreteClass2 extends AbstractClass
  22. {
  23. public function getValue() {
  24. return "ConcreteClass2";
  25. }
  26. public function prefixValue($prefix) {
  27. return "{$prefix}ConcreteClass2";
  28. }
  29. }
  30. $class1 = new ConcreteClass1;
  31. $class1->printOut();
  32. echo $class1->prefixValue('FOO_') ."\n";
  33. $class2 = new ConcreteClass2;
  34. $class2->printOut();
  35. echo $class2->prefixValue('FOO_') ."\n";
  36. // ConcreteClass1
  37. // FOO_ConcreteClass1
  38. // ConcreteClass2
  39. // FOO_ConcreteClass2
  1. <?php
  2. abstract class AbstractClass
  3. {
  4. // 我们的抽象方法仅需要定义需要的参数
  5. abstract protected function prefixName($name);
  6. }
  7. class ConcreteClass extends AbstractClass
  8. {
  9. // 我们的子类可以定义父类签名中不存在的可选参数
  10. public function prefixName($name, $separator = ".") {
  11. if ($name == "Pacman") {
  12. $prefix = "Mr";
  13. } elseif ($name == "Pacwoman") {
  14. $prefix = "Mrs";
  15. } else {
  16. $prefix = "";
  17. }
  18. return "{$prefix}{$separator} {$name}";
  19. }
  20. }
  21. $class = new ConcreteClass;
  22. echo $class->prefixName("Pacman"), "\n";
  23. echo $class->prefixName("Pacwoman"), "\n";
  24. // Mr. Pacman
  25. // Mrs. Pacwoman

接口 interface

对象接口

  • 使用接口(interface),可以指定某个类必须实现哪些方法,但不需要定义这些方法的具体内容。
  • 接口是通过 interface 关键字来定义的,就像定义一个标准的类一样,但其中定义所有的方法都是空的。
  • 接口中定义的所有方法都必须是公有,这是接口的特性。
  • 需要注意的是,在接口中定义一个构造方法是被允许的。在有些场景下这可能会很有用,例如用于工厂模式时。

实现(implements)

  • 要实现一个接口,使用 implements 操作符。类中必须实现接口中定义的所有方法,否则会报一个致命错误。类可以实现多个接口,用逗号来分隔多个接口的名称。
  • 在 PHP 5.3.9 之前,实现多个接口时,接口中的方法不能有重名,因为这可能会有歧义。在最近的 PHP 版本中,只要这些重名的方法签名相同,这种行为就是允许的。
  • 接口也可以继承,通过使用 extends 操作符。
  • 类要实现接口,必须使用和接口中所定义的方法完全一致的方式。否则会导致致命错误。

常量

接口中也可以定义常量。接口常量和类常量的使用完全相同,但是不能被子类或子接口所覆盖。

范例

  1. <?php
  2. // 声明一个'iTemplate'接口
  3. interface iTemplate
  4. {
  5. public function setVariable($name, $var);
  6. public function getHtml($template);
  7. }
  8. // 实现接口
  9. // 下面的写法是正确的
  10. class Template implements iTemplate
  11. {
  12. private $vars = array();
  13. public function setVariable($name, $var)
  14. {
  15. $this->vars[$name] = $var;
  16. }
  17. public function getHtml($template)
  18. {
  19. foreach($this->vars as $name => $value) {
  20. $template = str_replace('{' . $name . '}', $value, $template);
  21. }
  22. return $template;
  23. }
  24. }
  25. // 下面的写法是错误的,会报错,因为没有实现 getHtml():
  26. // Fatal error: Class BadTemplate contains 1 abstract methods
  27. // and must therefore be declared abstract (iTemplate::getHtml)
  28. class BadTemplate implements iTemplate
  29. {
  30. private $vars = array();
  31. public function setVariable($name, $var)
  32. {
  33. $this->vars[$name] = $var;
  34. }
  35. }

可扩充的接口

  1. <?php
  2. interface a
  3. {
  4. public function foo();
  5. }
  6. interface b extends a
  7. {
  8. public function baz(Baz $baz);
  9. }
  10. // 正确写法
  11. class c implements b
  12. {
  13. public function foo()
  14. {
  15. }
  16. public function baz(Baz $baz)
  17. {
  18. }
  19. }
  20. // 错误写法会导致一个致命错误
  21. class d implements b
  22. {
  23. public function foo()
  24. {
  25. }
  26. public function baz(Foo $foo)
  27. {
  28. }
  29. }

继承多个接口

  1. <?php
  2. interface a
  3. {
  4. public function foo();
  5. }
  6. interface b
  7. {
  8. public function bar();
  9. }
  10. interface c extends a, b
  11. {
  12. public function baz();
  13. }
  14. class d implements c
  15. {
  16. public function foo()
  17. {
  18. }
  19. public function bar()
  20. {
  21. }
  22. public function baz()
  23. {
  24. }
  25. }

使用接口常量

  1. <?php
  2. interface a
  3. {
  4. const b = 'Interface constant';
  5. }
  6. // 输出接口常量
  7. echo a::b;
  8. // 错误写法,因为常量不能被覆盖。接口常量的概念和类常量是一样的。
  9. class b implements a
  10. {
  11. const b = 'Class constant';
  12. }

抽象类和接口的区别

  1. 相同点:
  2. 1.都是用于声明某一种事物,规范名称、参数,形成模块,未有详细的实现细节。
  3. 2.都是通过类来实现相关的细节工作
  4. 3.语法上,抽象类的抽象方法与接口一样,不能有方法体,即{}符号
  5. 4.都可以用继承,接口可以继承接口形成新的接口,抽象类可以继承抽象类从而形成新的抽象类
  6. 不同点:
  7. 抽象类:是基于类来说,其本身就是类,只是一种特殊的类,不能直接实例,可以在类里定义方法,属性。类似于模版,规范后让子类实现详细功能
  8. 接口:主要基于方法的规范,有点像抽象类里的抽象方法,只是其相对于抽象方法来说,更加独立。可让某个类通过组合多个方法来形成新的类
  9. 1.接口是一种特殊的抽象类,接口中只包含抽象方法,没有成员属性
  10. 2.类实现(implements)接口时,必须完全实现接口中的所有方法;类继承(extends)抽象类时,只需对需要用到的抽象方法进行重写
  11. 3.抽象类只能单继承,但接口却是多继承,类对接口的实现也是多实现
  12. 4.接口本身就是抽象的,但注意不是抽象类,因为接口不是类,只是其方法是抽象的
  13. 抽象类和接口的结合:
  14. interface work{
  15. public function say();
  16. }
  17. abstract class a implements work{
  18. public function showlove(){
  19. echo 'love you<br />';
  20. }
  21. }
  22. class b extends a{
  23. public function say(){
  24. echo 'hello, i m in b';
  25. }
  26. }
  27. 接口和继承的结合:
  28. class b extends a implements kk{
  29. public function say(){
  30. echo '我是继承A类,同时实现say接口的<br />';
  31. }
  32. }

final 关键字

PHP 5 新增了一个 final 关键字。如果父类中的方法被声明为 final,则子类无法覆盖该方法。如果一个类被声明为 final,则不能被继承。

  1. <?php
  2. class BaseClass {
  3. public function test() {
  4. echo "BaseClass::test() called\n";
  5. }
  6. final public function moreTesting() {
  7. echo "BaseClass::moreTesting() called\n";
  8. }
  9. }
  10. class ChildClass extends BaseClass {
  11. public function moreTesting() {
  12. echo "ChildClass::moreTesting() called\n";
  13. }
  14. }
  15. // Results in Fatal error: Cannot override final method BaseClass::moreTesting()
  1. <?php
  2. final class BaseClass {
  3. public function test() {
  4. echo "BaseClass::test() called\n";
  5. }
  6. // 这里无论你是否将方法声明为final,都没有关系
  7. final public function moreTesting() {
  8. echo "BaseClass::moreTesting() called\n";
  9. }
  10. }
  11. class ChildClass extends BaseClass {
  12. }
  13. // 产生 Fatal error: Class ChildClass may not inherit from final class (BaseClass)

属性不能被定义为 final,只有类和方法才能被定义为 final。


参考资料:
sinat_38804294的博客
PHP7.0.x弃用的功能
PHP static
php CLASS、get_class()与get_called_class()的区别
后期静态绑定
abstract
interface
final

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注