又被直觉骗了——记一次静态变量在对象方法里的作用域测试

又被直觉骗了——记一次静态变量在对象方法里的作用域测试

Chris Yue 2 comments
Posts

我感觉现代的代码似乎用 static variable 越来越少了,无论是 PHP 别的语言,但我觉得,如果你清楚了 static variable 的萌点,还是挺好用的。

简单说来,用 static 修饰变量,可以让一个变量的生命周期在一个函数的范围之内变长,长到函数已经结束执行了,依然会为此变量保留内存,而目的往往是为了第二次调用同一个函数时,此变量能继续使用。所以,static 经常被用来做一些『缓存』优化:

不过面向对象的开发目前几乎已经空前的流行,似乎也很少有人直接用 function 了,所以上面的代码在当代往往是这样的:

那现在问题来了,如果我们需要两个数据库链接,我们通过创建两个数据库管理器能成功拿到吗?

请大家先猜测一下,$conn1 和 $conn2 是否为同一个对象?注意 getDbConn 函数可不是静态方法哦。

你们心中有个答案之后,我们可以用更简单的代码来验证猜测:

执行以上代码得到的结果是:

这说明,$foo 这个静态变量,在对象 $foo1 初始化过之后,$foo2 也是可以直接拿到的。

我们可以做这样的猜想,一个对象方法,其实跟函数没区别,只不过编译器可以自动给这个函数添加一个全局变量叫 $this 而已。所有的对象方法,在一个对象被初始化好之后,都不会为对象方法单独初始化空间,而是所有的对象都共用一个方法,而 static 变量,也就绑定在这个唯一的方法里。

之所以我在标题里没有提『PHP 的静态变量』是因为后来我查阅了一下其他语言,也都基本上是同样的行为。所以无论是 Java 也好还是 PHP,C++ 也好,千万要注意对象方法里的静态变量的生命域的问题。

文章到此你别以为结束了,如果我们把问题再搞复杂一点,加上继承关系,那又会出现什么样的情况呢?

大家先猜一猜?

上面代码的运行结果是:

看到这个结果,我们又可以猜想,当有继承发生的时候,通过继承,Foobar 复制了父类 Foo 的同名方法,虽然行为完全一致,但从内存的角度来看是两个不同的方法。

那如果问题再弄复杂点,bar 方法的可见性也做修改呢?

能猜到结果是什么吗?

实际运行下,得到

面对这个结果我们可以再一次做猜测:因为 private 申明,阻止了子类复制父类同名方法,所以这种情况下从内存的角度来讲,bar 方法依然只有一个。

文章总算是到结尾了,我们放慢速度,深入到代码里去,其实还是有很多可以琢磨和了解的细节的。这些细节可能看上去不是那么重要,但出现『意料之外的 bug』时,是很可能派上用场的哦。

最后,再问大家一个问题,如果上面的代码将 private 改成 protected,那结果又是什儿呢?你能猜对了吗?

又被直觉骗了——记一次静态变量在对象方法里的作用域测试 by Chris Yue is licensed under a Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International License.

微信赞赏码

文章不错,我要帮站长分担建站费!
天使投赏人

2 Comments

张帅

五月 20, 2018 在 9:28 下午

博主你的代码渲染用的什么插件啊?

    Chris Yue

    五月 21, 2018 在 12:15 下午

    crayon

     

发表评论

+ sixty seven = sixty nine