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

被刻印的时光 ゝ 提交于 2020-03-02 03:52:54

PHP的类及其实例:

<?php
class Myclass{
public $prop = 123;
}
$obj = new Myclass();
?> 

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

1、成员的访问控制

public:可以继承,可以在类的方法之外被访问,如$obj->prop;

protected:可以继承,不可以在类的方法之外被访问;

private:不可以继承,不可以在类的方法之外访问;

 

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

 

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

<?php
class Singleton{
private static $instance = null;
public $k = 88;
private function __construct(){
}
public static function getInstance(){
if(self::$instance == null){
//self::$instance = new Singleton();
self::$instance = new self();
}
return self::$instance;
}
public function __clone(){
throw new Exception('Singleton class can not be cloned');
return self::$instance;
}
}
//$in = new Singleton();
$in = Singleton::getInstance();
var_dump($in);
?> 

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

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

<?php
class Myclass{
public $prop = 123;
final public static function methodA(){
return 'this is a final method';
}
}
?> 

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

<?php
abstract class Myclass{
public $prop = 123;
abstract public function methodA();//抽象方法没有实现函数体
}
?> 

4、类的常量及其访问:类的常量不能使用访问控制修饰符,他是public的,可继承,可以被子类重写。访问类的常量必须使用双冒号::可以使用类名或类的实例(对象)来访问,因为是常量,所以名称不能使用表示变量的符号$。

<?php
class Myclass{
public $prop = 123;
const X = 999;
final public static function methodA(){
return 'this is a final method';
}
public function getConst(){
return self::X;//或者$this::X
}
}
$in = new Myclass();
echo $in->getConst();
echo Myclass::X;
?> 

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

<?php
class P{
const M = 100;
const N = self::M;
public static $k = 99;
public function getStatic(){
return self::$k;
}
public function getStatic2(){
return static::$k;
}
}
class S extends P{
const M = 200;
public static $k = 88;
public function getPConst(){
return parent::N;
}
}
$p = new P();
$s = new S();
echo $p::N.'<br>';//100
echo $s::N.'<br>';//200 该常量名继承自父类,在编译期就已经直接取 self::M 的值替换了 ,注意区别类的方法中使用 self::M
echo $s->getPConst().'<br>';//100
echo $s->getStatic2().'<br>';//88
?> 

5、类的静态成员访问

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

 

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

<?php
class Myclass{
public static $x = 99;
public function getX(){
return self::$x;
}
public static function getX2(){
return self::$x;
}
}
echo Myclass::$x."<br>";
echo Myclass::getX2()."<br>";
$MC = new Myclass();
echo $MC->getX2()."<br>";
?> 

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

<?php
class A{
public static $a1 = 11;
public $a2 = 22;
public static function showStatic(){
return self::$a1;
}
public function getStatic(){
return self::$a1;
}
public function getClassStatic(){
$className = get_called_class();
return $className::$a1;
}
public function getProp(){
return $this->a2;
}
}
 
class B extends A{
public static $a1 = 88;
public $a2 = 99;
}
 
$obj1 = new A();
$obj2 = new B();
 
echo $obj1->getStatic()."<br>";//11
echo $obj1->getClassStatic()."<br>";//11
echo $obj1->getProp()."<br>";//22
 
echo B::showStatic()."<br>";//11调用的是父类的方法,访问父类的静态成员
echo $obj2->getStatic()."<br>";//调用的是父类的方法,方法中的slef指向持有该静态方法的类
echo $obj2->getClassStatic()."<br>";//88
echo $obj2->getProp()."<br>";//99
?> 

 

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

public function getClassStatic(){

$className = get_called_class();

return $className::$a1;

}

//可以写成

public function getClassStatic(){

return static::$a1;

}

6、类的方法中几个指向类或实例的关键字

$this->propName    $this指向类的实例

parent::xxx      parent指向父类,可以访问父类的静态常量、静态属性,不能访问父类的非静态属性可以调用父类的方法(不能是private方法,无论是否静态)

self::xxx   self指向定义了当前被调用的方法的类,用于访问静态成员和类的常量

static::xxx  访问实例化了调用当前方法的实例的那个类,用于访问静态成员和类的常量,它跟self的差别是访问静态成员是“后期静态绑定”

 

7、类的继承中的重写问题

重写的成员的访问控制程度不能被缩小,例如,public的成员不能重写为protected

非静态成员不能重写为静态成员,静态成员也不能重写为非静态成员

 

8、接口中定义的方法必须是public

类在实现接口的方法时,这些方法也必须是public的。

接口也可以定义接口常量,用法和类的常量一致,但是接口不可以定义非函数成员。

接口与接口之间可以继承,接口的继承可以是多继承,用逗号隔开(子类与父类的继承是单继承)。

一个口可以实现多个接口,用逗号隔开。

<?php
interface Ix extends Iy,Iz{
   public function a();
}
 
 
class A implements Iy,Iz{
 
   //.......
 
}
?> 

9、类型约束

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

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

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

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

<?php
interface Im{
   public function a( classm $m);
}
 
 
 
class A implements Im{
    public function a($x){   // error ,参数$x必须限定为 classm 类型以匹配接口的定义
       var_dump($x);
    }
}
?> 

 

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!