静态属性属于类、所有实例共享,静态变量属于函数作用域、仅在该函数内跨调用保留值;二者初始化时机不同、不可互换,且均不可用表达式初始化。

静态属性属于类,静态变量属于函数作用域
静态属性是类的一部分,用 static 修饰的类成员,比如 public static $count,它绑定在类上,所有实例共享同一份内存;而静态变量只存在于函数内部,比如 function foo() { static $n = 0; },它的生命周期跨越多次调用,但作用域严格限制在该函数内,外部完全不可见。
常见错误现象:把 static $cache 写在方法里当成“类级缓存”,结果每次新建对象或不同请求中都各自维护一份,根本没共享——因为它压根不属于类,只是函数私有的状态。
- 静态属性可通过
self::$prop、static::$prop、ClassName::$prop访问 - 静态变量只能在定义它的函数体内通过变量名直接读写,无法从外部访问或重置
- 二者都不能用表达式初始化:
static $t = time()和public static $now = date('Y')都会报错
初始化时机和重置行为完全不同
静态属性在类首次被加载(如第一次使用 new、ClassName::xxx 或 autoloader 触发)时初始化一次,之后除非代码显式赋值,否则值一直保留;静态变量则在**第一次执行到该行声明时初始化**,后续进入函数直接跳过初始化,沿用上次退出时的值。
使用场景差异明显:静态属性适合做类级计数器(如已创建对象总数)、全局配置开关;静态变量适合避免重复计算(如解析配置文件一次后缓存结果),或函数内状态机(如分页迭代器)。
- CLI 模式下反复运行脚本:静态属性每次都是全新初始化(新进程),不是“没重置”,而是本来就没跨进程持久化
- FPM/Apache 下每个 HTTP 请求是独立进程/worker,静态变量和静态属性都会重来,别指望靠它们存用户会话数据
- 构造函数里递增
self::$count能统计实例数;但在普通方法里用static $i++只能统计该方法被调用了几次,和对象无关
序列化和继承中的表现截然不同
静态属性不会被 serialize() 保存,反序列化后恢复为类定义时的初始值(或未初始化状态);静态变量更彻底——它根本不在对象结构里,serialize() 完全感知不到它存在。
继承方面:子类不重定义同名静态属性时,self::$prop 和 static::$prop 在父类方法中行为不同;而函数内的静态变量不存在继承概念,子类方法里写的 static $x 和父类方法里的 static $x 完全是两个变量。
- 若父类有
static public $data = [],子类未重写,则ParentClass::$data和ChildClass::$data指向同一数组 - 但一旦子类声明了
static public $data = [],就变成独立副本,互不影响 - 函数内
static $buf在父类方法和子类方法中各有一份,哪怕方法签名相同、逻辑一致
容易被忽略的访问限制和性能影响
静态属性可以设为 private、protected,但静态变量没有访问控制——它天然是函数私有的,连 global 都不能跨函数共享它。
性能上,静态属性访问比普通属性略快(不经过对象查找),但差别微乎其微;真正要注意的是误用导致的逻辑 bug:比如在循环中反复 new 对象却依赖静态属性判断“是否首次”,结果发现 CLI 下值越积越大,其实是没理解进程隔离。
- 静态方法中不能用
$this,所以也不能通过$this->访问任何非静态属性或方法 - 静态属性不能用
->访问,写成$obj->count会报致命错误,必须用:: - PHP 8.1+ 的
readonly属性和静态属性互斥,readonly static是非法语法
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











