php中非静态方法的静态调用[期待拍砖]

ssseee000 2010-10-23 10:08:12
那天对这个主题比较感兴趣,于是就查了相关资料来记叙一下。不过,不知道自己写的到底有没有疏漏,所以,发到这里来给大家当靶子。如果您发现有什么叙述不正确的或不合理的,尽情拍砖——当然接受西红柿和鸡蛋。真理,只存在于辩论中。

---------------------------------------------------------------------------------
php中非静态方法的静态调用

参考资料:
1 Static method vs Non Static method
http://bytes.com/topic/php/answers/495206-static-method-vs-non-static-method

2 PHP static method performance
http://vega.rd.no/articles/php-static-method-performance


php是个很诡异的语言。当然,这是对学习过C++或者Java等面向对象语言的人来说。
php可以定义静态的方法,然后通过className::staticMethod()形式来调用。非静态的方法,当然通过classObject->nonStaticMethod()使用。这个其他语言中也是如此,没什么大惊小怪的。可是,您能用className::nonStaticMethod()调用非静态方法吗?这方面恐怕Java和C++要冷汗直流了。其实,php自己也是满脸黑线。为什么这么说呢?
先来看看面向对象的静态和非静态。面向对象的语言中,都会支持静态方法。静态方法,属于类的固定资产的;非静态的方法,属于类的实例的私有财产。在内存中,静态方法,对于整个类也就只存了这么一份;无论你new了多少个实例对象,它们共用的也就这么一份。对于非静态的就不一样了,你new几个,内存就给你new几份。另外,静态方法内不可以调用非静态方法,非静态方法内却可以调用静态方法。这个就是静态和非静态的区别。
面向对象用static关键字来定义静态。未被标明是静态的方法,是不可以用类名加两个冒号的形式调用的。php和其它很有区别的一点就是这个了:php中未被标明是静态的方法,也可以用类名加两个冒号的形式调用。那么为什么php有这种语法?又为什么感到很无奈呢?
-----以下说明php无奈的故事据相关资料改编,已经过演义处理--------
php之所以发展如此迅速,得益于它的速度。作为脚本语言,php追求高速度和简洁方便,所以选择了解释执行和使用过程方法。后来为了与国际接轨,引入了面向对象的概念。而就是在引入这个面向对象的特征时,发生了一件令php目瞪口呆,最终无可奈何的事情。面向对象有个很重要的概念就是继承。在继承中,子类如果覆盖父类的方法,同时需要调用父类的同名方法,该怎么办呢?php4版本提供了这样一种方法:parentClassName::method()。提出此种方法之时,有识之士已经发现了问题:这个调用方式,不正是静态方法的调用方式吗?php4随即答曰:不碍事。类中使用时,可以判断此方式为子类正在调用父类方法;非类中使用时,就判断为静态调用了。所需要的只是发现此种调用时查询一下方法映射就好了。其实,一想,也确实是那么回事。php4后来想想,如果每次调用都检验一下此次调用是否合法,那多少也会影响性能,不如这个问题就交给程序员去控制吧:他们会知道只在类中使用此形式的调用的。唉,可惜天不遂人愿。php4低估了程序员们的创造力!程序员们很快发现了这个方式,并且不余遗力地使用起来。许多集成的API也开始使用这种怪癖的方式。php无奈了,随即在php5中引入了另一种方式,使用关键字parent来调用父类函数:parent::method()。但是,想要放弃php的非静态方法的静态调用,着实是不再可能了。
--------------------------------------------------------
不过,话说回来,这种php的怪癖方式,有什么好处吗?性能和内存占用方面如何呢?
于是我开始推理了:定义了一个非静态的方法,静态调用时,php首先转换此方法为静态定义,加载入静态内存区域,然后执行。通常一次业务,只使用一个业务处理类中的一个方法,如果使用非静态定义,静态调用,内存中岂不是只加载了这个业务类中的一个方法,不是就实现了静态方法的按需加载吗?岂不是要省下一部分内存?性能方面,无论是静态调用,还是对象调用,反正都是执行一个方法,性能还不是一样?并且静态调用非静态定义方法还省了一个new语句。嗯,嗯。这么想的同时,手就开始写上了。
那么实际如何呢?我做了一个小测试。

t::start();t::end(); //消除t类首次加载的影响
t::start();
model_profile_base::getBaseInfo($uid);
t::end();

t::start();
$model = new model_profile_base();
$model->getBaseInfo($uid);
t::end();

model_profile_base是处理基本资料的一个业务类,比较复杂,比较接近于项目中的业务处理实际。
下面是用到的计时和统计内存的t类的定义:

<?php
function microtime_float()
{
list($usec, $sec) = explode(" ", microtime());
return ((float)$usec + (float)$sec);
}
class t{
static $start_time;
static $end_time;
static $start_memory;
static $end_memory;

public static function start()
{
self::$start_memory = memory_get_usage();
self::$start_time = microtime_float();
echo '<br/>Start @'.self::$start_time.'('.self::$start_memory.')|------->';
}

public static function end()
{
self::$end_time = microtime_float();
self::$end_memory = memory_get_usage();
echo 'End @'.self::$end_time.'('.self::$end_memory.') :';
echo '|======= 共耗时:'.(self::$end_time-self::$start_time).',共用内存:'.(self::$end_memory-self::$start_memory);
}
}

这样,只调用一次,结果如下:

Start @1287561987.1805(1008368)|------->End @1287561987.1806(1008368) :|======= 共耗时:3.2901763916016E-5,共用内存:0
Start @1287561987.1806(1008368)|------->End @1287561987.1938(1586452) :|======= 共耗时:0.013248920440674,共用内存:578084
Start @1287561987.1938(1586452)|------->End @1287561987.1945(1586652) :|======= 共耗时:0.00065183639526367,共用内存:200

第二行是静态调用非静态方法,第三行是正常调用非静态方法。然后,我发现我的推理悲剧了。刷了好几次页面,统计结果在数量级上都差不多。静态调用非静态方法无论内存占用还是性能上都不敢恭维。这样的结果有点令人咂舌。
那么,再试一下循环执行多次的结果:

t::start();t::end(); //消除t类首次加载的影响
t::start();
for($i=0; $i<1000;++$i) model_profile_base::getBaseInfo($uid);
t::end();

t::start();
$model = new model_profile_base();
for($i=0; $i<1000;++$i) $model->getBaseInfo($uid);
t::end();


于是更让人无语的结果出来了:

Start @1287562243.5799(1009372)|------->End @1287562243.5799(1009372) :|======= 共耗时:3.0040740966797E-5,共用内存:0
Start @1287562243.58(1009372)|------->End @1287562244.1532(1587544) :|======= 共耗时:0.57321000099182,共用内存:578172
Start @1287562244.1532(1587544)|------->End @1287562244.6921(1587744) :|======= 共耗时:0.53887605667114,共用内存:200

除了两种方式时间上开始接近外(并且还是正常调用比较利索),内存上仍然有天壤之别。失望之余,查了下网上,发现也有人做了类似的测试。我就直接把结果拷上来吧:
(可能光看结果,会感觉有点难于理解,可以在这里找到详细说明:http://vega.rd.no/articles/php-static-method-performance)

测试结果 (ORDER BY time DESC):

============Which method========================Time======
Inline calculation 0.0805 s
Normal function call 0.3438 s
Normal method called through object 0.4118 s
Static method called statically 0.4280 s
Unspecified method called through object() 0.4294 s
Unspecified method called statically() 0.6960 s

如此看来,静态调用非静态方法在性能和内存上都不占优势;另外,此种调用方法容易产生维护混乱。那么,来个短而有力的总结:静态调用非静态方法不可取。
...全文
1783 点赞 收藏 17
写回复
17 条回复
切换为时间正序
请发表友善的回复…
发表回复
志汉天涯 2013-01-28
楼主啊,叩拜啊~~真是太犀利,太精辟的讲解啦~~~~
回复
manbuyunhai1991 2012-12-18
现在没有了吧。。。。
回复
xl3417 2012-08-14
听君一席话,胜读十年书
回复
OnlyBlye 2012-01-02
虽然我已经很抵触上csdn了,但是看到这个答案,我还是要进来回一下。楼主清晰深入的剖析,消除了我的困惑,感谢楼主!
回复
fzy20080808 2011-05-30
学习中~~~~~·
回复
wu_rual123 2011-05-30
PHP这个调用我也一直没有弄清楚
就算它不是静态对象,一样可以用类名::方法名来调用
这样的话和实例化一个对象再去调用方法又有何区别。。。
php太神奇鸟~~~
回复
ora_xiao 2010-12-18
看到PHP,我还是很激动 .
回复
newing6 2010-12-17
学习了,很有说服力。
回复
「已注销」 2010-11-01
感觉PHP这样做是自寻死路。OOP我干什么放着JAVA不用 用你的PHP?
追求的不就是效率速度的问题
回复
inight0308 2010-10-30
[Quote=引用 5 楼 heyc1998 的回复:]

学习了!
[/Quote]
同上,希望能听到你的意见
回复
inight0308 2010-10-30
[Quote=引用 4 楼 php_thinker 的回复:]

学习了。
[/Quote]
哈哈,期待你的探讨
回复
inight0308 2010-10-30
[Quote=引用 3 楼 da_xeu 的回复:]

我就崩溃了,栽就栽在这个问题上了 很诡异的问题啊
[/Quote]
什么诡异的问题?这个应该只是调用方法的差异。我有点好奇了
回复
php_thinker 2010-10-29
学习了。
回复
在-云端 2010-10-29
学习了!
回复
puzzlex 2010-10-27
我就崩溃了,栽就栽在这个问题上了 很诡异的问题啊
回复
发动态
发帖子
基础编程
创建于2007-09-28

2.1w+

社区成员

从PHP安装配置,PHP入门,PHP基础到PHP应用
申请成为版主
社区公告
暂无公告