PHP 静态属性和静态方法
由 学院君 创建于10个月前, 最后更新于 7个月前
版本号 #2
2048 views
1 likes
0 collects
基本使用
前面介绍的 PHP 类属性和方法都要在类实例化后才能调用(常量属性除外),除此之外,PHP 还提供了静态属性和静态方法,所谓「静态」指的是无需对类进行实例化,就可以直接调用这些属性和方法,这么生讲有点硬,我们举个例子一看就明白了。
静态属性和方法的定义和调用
在 php_learning/oop 目录下新建一个 static.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 三种可见性级别。
调用另一个类的静态属性/方法
如果在一个类中调用其他类的静态属性和方法,需要通过 完整类名:: 进行引用:
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 类中常见的魔术方法。