欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  后端开发

PHP 类的变量与成员,及其继承、访问与重写时要注意的问题

程序员文章站 2022-05-03 19:25:20
...
  1. class Myclass{

  2. public $prop = 123;
  3. }
  4. $obj = new Myclass();

  5. ?>
复制代码

类的成员属性(属性的称呼相对于“方法”而言)包括类常量和类变量,其中类常量在定义时不可为空,类的属性在定义时如果被赋值,只能使用标量和数组,并且不能是表达式,因为类属性在编译期被初始化,PHP 在编译期不执行表达式。

1、成员的访问控制public:可以继承,可以在类的方法之外被访问 , 如 $obj->prop; protected:可以继承,不可以在类的方法之外被访问 private:不可以继承,不可以在类的方法之外访问

PHP 4 使用 var 来声明类的属性,在PHP5之后不再使用,PHP5.3之前使用被警告,PHP5.3之后可以用在 public 之前或单独使用作为 public 的别名。

这三个访问控制关键字也可以修饰构造函数,当 private 和 protected 修饰类的构造函数时,你只能通过一个 publice static 的静态方法来调用构造函数以实例化对象,因为够在函数无法在类之外被访问了,比如,单例类的实现:

  1. class Singleton {

  2. private static $instance=null;
  3. public $k = 88;
  4. private function __construct(){
  5. }

  6. public static function getInstance(){

  7. if(self::$instance==null){
  8. self::$instance = new self();
  9. }
  10. return self::$instance;

  11. }
  12. public function __clone(){ //pretend clone oprationg

  13. throw('Singleton class can not be cloned');
  14. return self::getInstance();
  15. }
  16. }
  17. //new Singleton(); // Error

  18. $in = Singleton::getInstance();
  19. ?>
复制代码

2、继承禁止: final 关键字,仅用于修饰类或类的方法

如果一个类被 final 修饰,这个类不能被继承,如果一个方法被final 修饰,则这个方法不能被子类重写(override)。

  1. class Myclass{
  2. public $prop = 123;
  3. final public static function methodA(){//不可继承的,公开的静态方法
  4. return 'this is a final method';
  5. }
  6. }
  7. ?>
复制代码

3、抽象类和抽象方法:abstract 仅用于 类和方法,抽象类不能直接用于实例化对象只能用于产生子类

  1. abstract class Myclass{
  2. public $prop = 123;
  3. abstract public function methodA(); //抽象方法没有实现函数体
  4. }
  5. ?>
复制代码

4、类的常量及其访问:类的常量不能使用访问限制修饰符,他是 public 的,可继承,可以被子类重写,访问类的常量必须使用双冒号 :: ,可以使用类名或类的实例来访问。

  1. class Myclass{

  2. public $prop = 123;
  3. const x =999;
  4. public static function methodA(){

  5. return 'this is a final method';
  6. }
  7. public function getConst(){

  8. return self::x; //或者 $this::x;
  9. }
  10. }
  11. $instance = new Myclass();

  12. echo Myclass::x;

  13. echo $instance::x;
  14. echo $instance->getConst();
  15. ?>
复制代码

类的常量是一个值,在代码编译期常量名被替换为相应的值,在运行期不可修改,因此,类的常量是与类本身相关,在实例化对象之前就已经存在了,因此类的常量可以直接使用类名访问。

  1. class P{

  2. const m = 100;
  3. const n = self::m;
  4. }
  5. class S extends P{

  6. const m=200;
  7. public function getPConst(){
  8. return parent::n;
  9. }
  10. }
  11. $p = new P();

  12. $s = new S();
  13. echo $p::n; //100
  14. echo $s::n; //200 该常量名继承自父类,在编译期取 self::m 的值替换,注意区别类的方法中使用 self::m
  15. echo $s->getPConst(); //100

  16. ?>
复制代码

5、类的静态成员及访问

static 可 以修饰类的属性及方法,被 static 修饰的成员属于类而不属于类的实例,静态成员必须使用类名加双冒号 :: 来访问, 因为在实例化对象之前 静 态成员就存在了,因此,在静态方法内,禁止使用指向实例本身的伪变量 $this(或习惯上称为 $this 指针) ,可以使用关键字 self 代替 类名(相当于类的魔术常量 __CLASS__)。

static 不能用于修饰 类的构造函数,也不能用于修饰接口声明的方法。

  1. class Myclass{

  2. public static $x = 99;
  3. public function getX(){

  4. return self::$x;
  5. }
  6. }
  7. echo Myclass::x; //99

  8. ?>
复制代码

静态成员可以使用 访问控制关键字修饰,可以被继承和重写,需要注意的是,如果一个子类继承了父类的静态方法(没有重写该方法),那么子类调用的实际是父类的静态方法。因为静态成员持有者是类不是对象,所以类的多个实例是共享同一个静态属性的,在一个实例中修改静态属性会影响到另一个实例中的静态属性:

  1. class A{

  2. public static $a1 = 11;

  3. public $a2 = 22;
  4. public static function showStatic(){

  5. return self::$a1;
  6. }
  7. public function getStatic(){

  8. return self::$a1;
  9. }
  10. public function getClassStatic(){

  11. $className = get_called_class();
  12. return $className::$a1;
  13. }
  14. public function getProp(){

  15. return $this->a2;
  16. }
  17. }
  18. class B extends A{

  19. public static $a1 = 88;
  20. public $a2 = 99;
  21. }
  22. $obj1 = new A();

  23. $obj2 = new B();
  24. echo A::showStatic(); //11

  25. echo $obj1->getStatic(); //11
  26. echo $obj1->getClassStatic(); //11
  27. echo $obj1->getProp(); //22
  28. echo B::showStatic(); //11 调用的是父类的方法,访问父类的静态成员

  29. echo $obj2->getStatic(); //11 调用的是父类的方法,方法中的 self 指向持有该静态方法的类
  30. echo $obj2->getClassStatic(); //88
  31. echo $obj2->getProp(); //99
  32. ?>
复制代码

后期静态绑定:为了避免子类重写静态属性后,使用继承来的方法仍然访问父类的静态属性, PHP5.3 增加了一个新的语法:后期静态绑定,使用 static 关键字替代 self 关键字,使得 static 指向与 get_called_class() 返回的相同的类,即当前调用该静态方法的对象所属的类,该关键字对于 静态方法的访问同样有效。

  1. public function getClassStatic(){

  2. $className = get_called_class();
  3. return $className::$a1;
  4. }
  5. //可以写成 :

  6. public function getClassStatic(){
  7. return static::$a1;
  8. }
  9. //用于静态方法

  10. //A类中:
  11. public static function testStatic(){
  12. echo "

    testStatic of A

    ";
  13. }
  14. public function callStatic(){

  15. static::testStatic();
  16. }
  17. //B类中:

  18. public static function testStatic(){
  19. echo "

    testStatic of B

    ";
  20. }
  21. //B类继承A类的 callStatic 方法,可以正确访问各自类的 testStatic 方法。
  22. ?>
复制代码

6、类的方法中几个指向类或实例的关键字 $this->propName $this 指向类的实例 parent::xxx parent 指向父类,可以访问父类的静态常量、静态属性(parent::$xxx) ,不能访问父类的非静态属性 ,可以调用父类的方法(不能是 private 方法,无论是否静态) self::xxx self 指向定义了当前被调用的方法的类,用于访问静态成员和类的常量 static::xxx 访问实例化了调用当前方法的实例的那个类,用于访问静态成员和累的常量,他跟 self 的差别是访问静态成员时采用 “后期静态绑定”。

7、类的继承中的 重写问题: 重写的成员的访问控制程度不能被缩小,例如, public 的成员不能重写为 protected 非静态成员不能重写为静态成员,静态成员也不能重写为非静态成员

8、接口中定义的方法必须是 public 类在实现接口的方法时,这些方法也必须是 public 的,具体实现的(不能是 abstract )。 接口也可以定义接口常量,用法与类常量完全一致,但是接口不可以定义非函数成员。 接口与接口之间可以继承,接口的继承可以是多继承,用逗号隔开(字类与父类的继承是单继承的) 一个类可以实现多个接口,用逗号隔开

  1. interface Ix extends Iy,Iz{
  2. public function a();
  3. }
  4. class A implements Iy,Iz{
  5. .......
  6. }
  7. ?>
复制代码

9、类型约束

PHP 的函数(或类的方法) 可以在声明时限定参数的类型,但只能 限定 array 或 object(class/interface) ,如果限定为 string 型, PHP 会认为是限定为一个 string 类 的 object 参数。

如果类型被限定为某个接口,则传入的参数必须是实现该接口的类的实例。

在接口实现、子类重写父类方法时,不能修改已经限定的参数类型。

在方法、函数调用时,如果传入了与限定的参数类型不同的数据将会报错,但是可以接受 null 参数。

  1. interface Im{

  2. public function a( classm $m);
  3. }
  4. class A implements Im{

  5. public function a($x){ // error ,参数$x必须限定为 classm 类型以匹配接口的定义
  6. var_dump($x);
  7. }
  8. }
  9. ?>
复制代码