蹉跎莫遣韶光老,人生唯有读书好。这篇文章主要讲述Groovy踩坑记之方法调用八层认识相关的知识,希望能为你提供帮助。
这个问题源于某一次性能测试中写了一个异步显示QPS的功能,思路是在动态性能测试模型中异步线程中增加输出QPS的能力。就是获取1s内发出去的请求,然后当做实时QPS输出。
但是在实际使用中,每次输出的QPS只有1,这就特别尴尬了。如果不输出日志信息QPS就是正常的。经过查看线程转储和使用jconsole
分析,发现每次执行相关方法的时候执行线程被锁住了。
经过了百思不得其解,然后终于彻头彻尾地悟道,这原来是Groovy其中一个特性导致的BUG。
第一层由于原框架比较复杂,这里分享一个复现的Demo文件。
首先有一个父类Parent
和子类Child
,父类有一个静态test
方法,子类有一个静态getTest
方法。然后子类有一个成员方法bugs
,改方法调用了test
方法。
class FunTester public static void main(String[] args)
new Child().bugs()private static class Child extends Parent void bugs()
println(test(12))static Child getTest()
println("子类方法 无参数")
retrun new Child()private static class Parent static def test(int i)
return "父类方法 参数$i"
你们猜一猜控制台输出什么,我是想破头也想不出来:
子类方法 无参数
父类方法 参数12Process finished with exit code 0
居然先调用了子类的
getTest
方法,再去调用的父类方法,太神奇了。第二次我们改动一下
getTest
的返回值,看看下面的代码:class FunTester public static void main(String[] args)
new Child().bugs()private static class Child extends Parent void bugs()
println(test(12))static String getTest()
println("子类方法 无参数")
return "FunTester"private static class Parent static def test(int i)
return "父类方法 参数$i"
控制台如下:
子类方法 无参数
父类方法 参数12Process finished with exit code 0
如果我们把返回值改成
Groovy
关键字def
,方法如下:static def getTest()
println("子类方法 无参数")
return "FunTester"
控制台输出:
子类方法 无参数
父类方法 参数12Process finished with exit code 0
输出逻辑没有问题,依然是先调用了子类的方法。
第三层这次我们把返回值改成
void
,看看效果如何:这里就不放全部代码了,只展示改动部分。
static void getTest()
println("子类方法 无参数")
控制台输出:
父类方法 参数12Process finished with exit code 0
终于恢复正常了。
第四层如果我们这次恢复
def
返回值,改动一下getTest
的方法名,如下:static def getTeest()
println("子类方法 无参数")
控制台输出:
父类方法 参数12Process finished with exit code 0
也是正常的,跟java无异。
到现在不知道读者是否有点迷糊。下面我们看一下更进一步测试。
第五层我们先恢复异常时候的代码,然后改动
bugs
方法的使用test
时候的参数。改动如下:void bugs()
println(test("FunTester"))static def getTest()
println("子类方法 无参数")
控制台输出如下:
子类方法 无参数
Exception in thread "main" groovy.lang.MissingMethodException: No signature of method: static com.funtest.groovytest.FunTester.test() is applicable for argument types: (String) values: [FunTester]
Possible solutions: getAt(java.lang.String), wait(), use([Ljava.lang.Object;
), wait(long), tap(groovy.lang.Closure), is(java.lang.Object)
-------省略错误信息-----------
at com.funtest.groovytest.FunTester.main(FunTester.groovy:6)Process finished with exit code 1
看样子是先调用的子类方法
getTest
,然后去调用父类方法,因为参数类型不一样导致了一个这个报错。第六层下面到了解密时刻,起因是因为
Groovy
特性中的两点:- 如果有
get
方法,会隐性给当前类或者当前对象增加一个属性或者变量。 - 当前方法调用出开始,会寻找最近的方法调用,这里只看方法名是否一致或者符合
get
+方法名首字母大写的方法尝试寻找符合的方法调用 - Groovy语言中,会把闭包和通常变量命令方式无异,而且Groovy语言检查中并不会检查这个。
第七层以上Demo中
test(12)
这个方法调用,通常理解为:调用test
方法,参数是FunTester
,然后在子类中找test
方法,结果没找到。就去找父类方法,然后找到了,而且参数数量和类型匹配,所以会调用父类的test
方法。这个也是Java
的思路。第八层但是在Groovy世界中,
test(12)
还有另外一层理解:test
理解为Child
一个静态属性/变量,或者一个对象属性/变量(因为bugs
是个成员方法)。- 然后
test(12)
调用,先去找当前子类的test
属性,然后把test
对象当做闭包,去调用call(12)
。 - 由于
Groovy
特性,子类有个方法getTest
,所以有了隐性的test
属性。所以会先调用getTest
方法。 - 如果
getTest
返回void
时,那么getTest
就是不符合Groovy
语言中GET
方法,所以在子类找不到test
属性定义方法,只能去父类找响应的方法。
Have Fun ~ Tester !
- FunTester原创大赏
- 性能测试专题【FunTester原创】
- 接口功能测试专题【FunTester原创】
- Java、Groovy、Python和Golang如何把方法当作参数
- 下单延迟10s撤单性能测试
- 自动化如何选择用例
- Java& Go高性能队列之channel性能测试
- 动态模型之动态增减【FunTester测试框架】
- 白盒测试扫盲
- 6个重要的JVM性能参数
- Java& Go三种HTTP客户端性能测试
- 测试人员常用借口
- 又双叒叕一行代码:Map按值排序
- 基于爬虫的测试自动化经验分享
- 利用闭包实现自定义等待方法
推荐阅读
- python 包之 redis 数据库操作教程
- #yyds干货盘点# 解决华为机试(字符统计)
- 数仓建设 | ODSDWDDWM等理论实战(好文收藏)
- MySQL事务基础知识总结与实践操作
- Python爬虫120例之第20例,1637一路商机网全站加盟数据采集
- Win10系统UWP应用系统设置打开命令有哪些?
- win10系统如何完成多核打开?
- Win10实用小技巧:这样也可以关机
- Win10游戏模式是啥?Wn10游戏模式打开设置图文详细教程