PHP 静态属性和静态方法
基本使用
前面介绍的 PHP 类属性和方法都要在类实例化后才能调用(常量属性除外),除此之外,PHP 还提供了静态属性和静态方法,所谓「静态」指的是无需对类进行实例化,就可以直接调用这些属性和方法,这么生讲有点硬,我们举个例子一看就明白了。
静态属性和方法的定义和调用
在 php_learning/oop
目录下新建一个 static.php
文件,编写一段测试代码如下:
<?php
class Car
{
public static $WHEELS = 4;
public static function getWheels()
{
return self::$WHEELS;
}
}
在 PHP 中,我们通过 static
关键字来修饰静态属性和方法,这里我们定义了一个静态属性$WHEELS
和静态方法 getWheels
,由于静态属性和方法可以直接通过类引用,所以又被称作类属性和类方法(相应的,非静态属性和非静态方法需要实例化后通过对象引用,因此被称作对象属性和对象方法),静态属性和方法可以通过 类名::属性/方法
的方式调用:
echo "WHEELS:" . Car::$WHEELS . PHP_EOL;
echo "getWheels:" . Car::getWheels() . PHP_EOL;
如果是在类内部方法中,需要通过 self::
引用当前类的静态属性和方法,就像常量一样,因为静态属性和方法无需实例化类即可使用,而没有实例化的情况下,$this
指针指向的是空对象,所以不能动过它引用静态属性和方法:
同理,我们也不能在静态方法中通过 $this
引用对象属性和方法。
静态属性支持动态修改
为了以示区别,这里通过了大写字母设置静态属性变量名,这不是强制的,因为静态属性名和常量不同,可以在运行时进行修改,只是它的作用域是整个类,而不是某个对象:
Car::$WHEELS = 8;
echo "getWheels:" . Car::getWheels() . PHP_EOL;
上述代码的打印结果是:
getWheels:8
并且静态属性和方法与对象属性和方法一样,支持设置 private
、protected
、public
三种可见性级别。
调用另一个类的静态属性/方法
如果在一个类中调用其他类的静态属性和方法,需要通过 完整类名::
进行引用:
<?php
class Gas
{
public static $POWER = '汽油';
}
class Car
{
protected static $WHEELS = 4;
public static function getWheels()
{
return self::$WHEELS;
}
public static function printCar()
{
printf("这辆车有 %d 个轮子,使用 %s 作为动力来源\n", self::$WHEELS, Gas::$POWER);
}
}
Car::printCar();
这里我们通过 Gas::$POWER
引用了 Gas
中的静态属性 $POWER
,静态方法也是类似,上述代码打印结果如下:
在非静态方法中调用静态属性/方法
另外,我们前面提到不能在静态方法中通过 $this
调用非静态属性/方法,但是在非静态方法中可以通过 self::
调用静态属性/方法:
class Car
{
...
public static function printCar()
{
return sprintf("这辆车有 %d 个轮子,使用 %s 作为动力来源\n", self::$WHEELS, Gas::$POWER);
}
public function __toString()
{
return self::printCar();
}
}
$car = new Car();
echo $car;
这很好理解,因为前者是因为没有实例化就可以调用,后者实例化后不影响类方法的调用,在上述代码中,我们将静态方法 printCar
调整为通过 sprintf
函数返回格式化字符串,然后在魔术方法 __toString
方法中调用,作为该非静态方法的返回值。上述代码的打印结果是:
完全可以正常运行。
进阶功能
静态方法的继承和重写
和非静态属性/方法一样,静态属性和方法也可以被子类继承,静态方法还可以被子类重写:
class Car
{
...
public static function getClassName()
{
return __CLASS__;
}
public static function who()
{
echo self::getClassName() . PHP_EOL;
}
}
class LynkCo01 extends Car
{
public static function getClassName()
{
return __CLASS__;
}
}
通过 __CLASS__
可以获取当前类的类名,我们分别调用两个类的 getClassName
方法:
echo Car::getClassName() . PHP_EOL;
echo LynkCo01::getClassName() . PHP_EOL;
打印结果如下:
说明子类重写了父类的同名静态方法,同样我们在子类上也可以调用父类中的 who
方法:
Car::who();
LynkCo01::who();
上述代码的打印结果是:
Car
Car
咦?为什么第二个打印的结果是父类名 Car
而不是子类名 LynkCo01
?这是因为,和 $this
指针始终指向持有它的引用对象不同,self
指向的是定义时持有它的类而不是调用时的,为了解决这个问题,从 PHP 5.3 开始,新增了一个叫做后期静态绑定的特性。
后期静态绑定
后期静态绑定(Late Static Bindings)针对的是静态方法的调用,使用该特性时不再通过 self::
引用静态方法,而是通过 static::
,如果是在定义它的类中调用,则指向当前类,此时和 self
功能一样,如果是在子类或者其他类中调用,则指向调用该方法所在的类,我们通过后期静态绑定改写上述代码:
class Car
{
...
public static function getClassName()
{
return __CLASS__;
}
public static function who()
{
echo static::getClassName() . PHP_EOL;
}
}
class LynkCo01 extends Car
{
public static function getClassName()
{
return __CLASS__;
}
}
...
Car::who();
LynkCo01::who();
再次执行,打印结果如下:
Car
LynkCo01
表明后期静态绑定生效,即 static
指向的是调用它的方法所在的类,而不是定义时,所以称之为后期静态绑定。
此外,还可以通过 static::class
来指向当前调用类的类名,例如我们可以通过它来替代 __CLASS__
,这样上述子类就没有必要重写 getClassName
方法了:
class Car
{
...
public static function getClassName()
{
return static::class;
}
public static function who()
{
echo static::getClassName() . PHP_EOL;
}
}
class LynkCo01 extends Car
{
}
代码执行结果和之前一样。
同理,self::class
则始终指向的是定义它的类,感兴趣的同学可以自行测试,这里不再演示了。self
和 static
各有其使用场景,后面我们会看到其实际的使用场景。
关于 PHP 静态属性和方法的使用就简单介绍到这里,明天,学院君将给大家介绍下 PHP 类中常见的魔术方法。
3 Comments
如果每一个小阶段学完,能提供一个小demo作业就好了
1楼的想法很棒
确实有的 在知识星球群里