OO第三单元总结
JML理论基础和应用工具链
- JML定义
JML(Java Modeling Language)是用于对Java程序进行规格化设计的一种表示语言。JML是一种行为接口规格语言(Behavior
Interface Specification Language, BISL),基于Larch方法构建。
BISL提供了对方法和类型的规格定义手段。所谓接口即一个方法或类型外部可见的内容。 - JML表达式
原子表达式:
\result:表示一个非 void 类型的方法执行所获得的结果,即方法执行后的返回值。 \old(expr):表示一个表达式expr在相应方法执行前的取值,该表达式涉及到评估expr中的对象是否发生变化。 如果是引用(如hashmap),对象没改变,但进行了插入或删除操作。v和odd(v)也有相同的取值。 \not_assigned(x,y,...):用来表示括号中的变量是否在方法执行过程中被赋值。如果没有被赋值,返回为true ,否则返回 false 。用于后置条件的约束,限制一个方法的实现不能对列表中的变量进行赋值。 \not_modified(x,y,...):该表达式限制括号中的变量在方法执行期间的取值未发生变化。 \nonnullelements(container):表示container对象中存储的对象不会有null。 \type(type):返回类型type对应的类型(Class),如type(boolean)为Boolean.TYPE。TYPE是JML采用的缩略表示,等同于Java中的 java.lang.Class。 \typeof(expr):该表达式返回expr对应的准确类型。如\typeof(false)为Boolean.TYPE。
量化表达式:
\forall:全称量词修饰的表达式,表示对于给定范围内的元素,每个元素都满足相应的约束。
\exists:存在量词修饰的表达式,表示对于给定范围内的元素,存在某个元素满足相应的约束。
\sum:返回给定范围内的表达式的和。
\product:返回给定范围内的表达式的连乘结果。
\max:返回给定范围内的表达式的最大值。
\min:返回给定范围内的表达式的最小值。 - 方法规格
前置条件:对方法输入参数的限制,如果不满足前置条件,方法执行结果不可预测,或者说不保证方法执行结果的正确性。
后置条件:对方法执行结果的限制,如果执行结果满足后置条件,则表示方法执行正确,否则执行错误。其中ensures是JML关键词,表达的意思是“方法实现者确保方法执行返回结果一定满足谓词P的要求,即确保P为真”。并列关系和或关系与前置相同。
副作用:副作用指方法在执行过程中会修改对象的属性数据或者类的静态成员数据,从而给后续方法的执行带来影响。 - 类型规格
不变式(invariant)是要求在所有可见状态下都必须满足的特性,其中invariant为关键词, P为谓词。对于类型规格而言,可见状态(visible state)是一个特别重要的概念。
- 应用工具链
- OpenJML:OpenJML最基本的功能就是对JML注释的完整性进行检查。检查包括经典的类型检查、变量可见性与可写性等。通过命令行使用OpenJML时,可以通过-check参数(缺省)指定类型检查。
- JUnit:JUnit是由 Erich Gamma 和 Kent Beck 编写的一个回归测试框架(regression testing framework)。Junit测试是程序员测试,即所谓白盒测试,因为程序员知道被测试的软件如何(How)完成功能和完成什么样(What)的功能。Junit是一套框架,继承TestCase类,就可以用Junit进行自动测试了。
部署JMLUnitNG/JMLUnit:
对于Junit测试,我并不会自动生成测试样例,而是生成测试类然后自己修改test方法,通过自己构造测试样例。
作业分析
- 第一次作业
-
第一次作业就是正常按照JML写,由于第一次作业比较简单,就有点掉以轻心导致出了一个小bug,然后强测全部炸了,具体代码的架构如下
-
UML类图:
-
bug分析:第一次作业的bug是iscircle方法,我在iscircle方法里写了一个DFS的方法,传参是时应该是id1和1d2,结果串成了两个id1,
但是中测过了,所以我就没注意到这个bug,导致强测炸了。
-
- 第二次作业
-
第二次作业时我修改了第一次作业的bug,也是照着JML写,这一次的难度比上一次简单一些,同样很简单的中测过了,但是强测又炸了。
-
UML类图
-
bug分析:第二次的bug出现在addPerson输出exception的时候,我在一处判断条件下,当人数大于1111时,我会输出一个exception,但是正常的处理是忽略,并且不输出。
导致我输出会多一些。同时还有CPU时间超时,因为我的容器选择并不好,第二次的作业并没有考算法。
-
- 第三次作业
-
同样的,在第三次作业之前我又修改了第二次的bug,对于超时问题,我加入了hashMap,id和人对应,查询时间比arrayList更短。同时修改了一些方法,来缩短CPU时间。但是第三次作业考的算法就比较难了。
比如查询最短路和查询强连通,我都用了非常笨的方法,导致有很多点CPU超时。 -
UMl类图如下
-
bug 分析
最后一次作业我的方法虽然很笨,但是应该都是对的,但是CPU耗时太长了,所有的错都是CPU超时。查询最短路径应该用迪杰斯特拉算法,强连通应该用tarjan算法。
-
心得体会
通过这次作业,我深刻的体会到了自己构造测试样例的重要性,这一单元是我到目前做的最糟糕的一个单元,弱测中测相当于没有测,以前我太依赖一开始的评测来找错误,这一次什么错都没有,
但是强测次次都很垃圾,第一次出错是因为我的粗心,本来很简单的第一次作业,id2和id1写错了,第二次作业则是没有正确的实现JML的要求,同时容器选择太笨了,第三次作业则是算法问题。
这一个单元,我基本都是这一次的作业修改上一次的bug,最后一次虽然保证了正确性,可是算法太垃圾了,这有一部分原因是因为
我忘记了图一部分的知识。下一个单元我会细心code,认真的在网上学习算法的。