OO第三单元总结

JML理论基础

JML表达式

  • JML的表达式是对Java表达式的扩展

    • 新增了一些操作符和原子表达式。

  • 优先级

    • 见JML语言手册12.3节(Expression)获得完整的优先级列表。

  • 新增的表达式成分仅用于JML中的断言(assertion)语句和其他相关的注释体

  • 特别注意

    • 在JML断言中,不可以使用带有赋值语义的操作符,如 ++,--,+= 等操作符

原子表达式

  • \result表达式

    • 个非 void 类型的方法执行所获得的结果,即方法执行后的返回值

  • \old( expr )表达式

    • 一个表达式 expr 在相应方法执行前的取值。

    • 遵从Java的引用规则

      • 都应该使用\old把关心的表达式取值整体括起来。

  • \not_assigned(x,y,...)表达式

    • 来表示括号中的变量是否在方法执行过程中被赋值。

      • 没有被赋 值,返回为 true ,否则返回 false

    • 主要用于后置条件的约束表示上

      • 限制一个 方法的实现不能对列表中的变量进行赋值

  • \not_modified(x,y,...)表达式

    • 限制括号中的变量在方法 执行期间的取值未发生变化

  • \nonnullelements( container )表达式

    • 表示 container 对象中存储的对象不会有 null

  • \type(type)表达式

    • 返回类型type对应的类型(Class)

    • JML 采用的缩略表示

      • 等同于Java中的 java.lang.Class

  • \typeof(expr)表达式

    • 式返回expr对应的准确类型。

量化表达式

  • \forall表达式

    • 全称量词修饰的表达式

  • \exists表达式

    • 存在量词修饰的表达式,

  • \sum表达式

    • 返回给定范围内的表达式的和

  • \product表达式

    • 返回给定范围内的表达式的连乘结果

  • \max表达式

  • \min表达式

  • num_of表达式

    • 返回指定变量中满足相应条件的取值个数

集合表达式

  • 集合构造表达式

    • 可以在JML规格中构造一个局部的集合(容器),,明确集合中可以包含的元素

    • :new ST {T x|R(x)&&P(x)},

      • R(x)对应集合中x的范围

      • P(x)对应x取值的约束

操作符

  • 正常使用java中操作符

    • 括算术操作符、逻辑预算操作符等

  • 专门定义的操作符

    • 子类型关系操作符

      • E1<:E2

        • 子类型(sub type),则该表达式的结果为真

        • 是相同的类型,该表达式的结果也为真

        • 任意一个类X,都必然满足 X.TYPE<:Object.TYPE

    • 等价关系操作

      • b_expr1<==>b_expr2 或者 b_expr1<=!=>b_expr2

      • 中b_expr1和b_expr2都 是布尔表达式

      • <==> 比 == 的优先级要低

    • 推理操作符

      • b_expr1==>b_expr2 或者 b_expr2<==b_expr1

    • 变量引用操作

      • 关键词

        • \nothing指示一个空集

        • \everything指示一个全集

      • 变量引用操作符经常在assignable句子中使用

应用工具链

JMLUnitNG

可以根据JML生成一个java类文件测试的框架,结合openjml的-rac运行时检查选项,实现对代码的自动化测试。

OPENJML

在Intellij中安装openJML插件,可以进行规格检查。

JMLUnitNG测试

测试发现其能够构造一些数据,但是并没有构造边界数据的能力,一定程度上有作用,但是不能完全依靠自动评测。

架构设计

第一次作业

要求

第一次作业简单地实现了一个一个社交网络系统,包括两个接口Person和Network,我们需要根据JML规格实现对应的接口,完成相应的代码,并在main函数中运行以验证结果。

UML图

OO第三单元总结_第1张图片

第一次作业比较简单,基本就是阅读JML然后写代码就行了。

大部分的集合类的数据结构我都是使用ArrayList来实现的。

唯一需要动心思的地方是isCir函数,这里我使用了数据结构课上学习过的深度优先搜索算法,在MyNetwork中我使用Boolean[] Marked来判定是否被访问过。

第二次作业

要求

第二次作业在第一次作业的基础上增加了Group接口,需要实现Group中的方法和Network中的增加的方法。

UML图

OO第三单元总结_第2张图片

第二次作业看起来并不难,但是强测指令数量可达10万条,而且对CPU时间有了限制。

由于涉及到许多查询的操作,我放弃了ArrayList转而投向HashMap的怀抱,大部分的HashMap都是以PersonId为Key,这样就能减少时间复杂度。在了解到HashMap的复制和扩容机制后,我还设置了部分HashMap的初始化容量,以空间换时间。

另外,我听说深度优先搜索可能会爆栈,于是改用了广度优先搜索bfs,bfs实质上是一个栈的数据结构,不用递归。我还放弃使用了Boolean[] Marked,而是在每一个Person中都增加了一个Boolean Marked变量,便于管理。

第三次作业

要求

第三次作业没有增加新的接口,只增加了部分方法。

UML图

OO第三单元总结_第3张图片

第三次作业大部分代码比较好写,需要注意的只有queryBlockSum、queryMinPath和queryStrongLinked方(以下使用缩写简称)。

qbs的JML阅读起来比较困难,一开始不太好懂,但稍加思考就会发现这就是求连通块的数量,再回头看方法名,不就是BlockSum吗?正解。我还是基于广度优先搜索实现的,采用填充的方式,对每个person遍历,如果该person没有被访问过,则进行广度优先搜索,并标记该person所有的acquaintance,这样进行广度优先搜索的次数刚好是连通块的数目。

qmp则是典型的迪杰斯特拉算法问题。在这里我并没有使用堆优化,当然这是由于我很菜。

qsl则是一个点双连通分量的问题,分为两种情况来处理。第一种情况是p1和p2是朋友关系,则先删除其朋友关系,再判断是否linked,最后恢复朋友关系。第二种情况是,p1和p2不是直接朋友关系,但他们之间通过一些其他人联系着,这时p1和p2之间有很多中间朋友,删除任意一个朋友,看p1和p2是否仍isCircle,如果是则返回true,否则返回false。

bug分析

第一次作业

第一次作业强测只拿了20分,互测未被hack。

原因出在深度优先搜索dfs上,我将当时数据结构上课的代码生搬硬套地拿了过来,当时是基于链表实现的有向图,而作业中是无向图,有向图可以采用marked=true;dfs();marked=false;这样的结构来优化,而无向图可能会出现死循环,我没有注意到这一点。

后来我发现其实是不用这么“优化”的,直接dfs就好了,我可能是个撒贝吧。

第二次作业

第二次作业强测拿到80分,互测被hack1次。

原因果然出在时间复杂度上,在queryGroupValueSum和queryGroupRelationSum的时候,我单纯地就照着JML的写了,每次查询都用了若干个循环。

后来我用了缓存和更新制,只在初始化和变化时才更新值,查询就直接返回,减少了时间复杂度。

第三次作业

第三次作业强测拿到85分,互测未被hack。

原因出在queryMinPath上,我没写堆优化的迪杰斯特拉算法,然后就被卡时间了,当时确实没搞懂怎么堆优化。

可能我就是菜吧。

心得体会

在刚刚接触JML时我确实是有一点抵触的,这不就是纯阴间玩意儿嘛。但是写作业时却发现,作业还挺好写的。

这几次作业的难点其实都在数据结构和离散上面,看来所有的知识都是互通的,前面欠的总有一天要还的。

JML在于给出规范,可能在目前来说对我们没有特别明显的作用,但是相信在工程开发中,一个严格的规范是开发的必然标准,学习规范绝对有非常有用的。

不知不觉OO课程就过去一大半了,这一路,我虽然并不是很出色,但是大部分作业都还能拿一个高分,也从来没有出现过无效作业,他们说OO很难,每周熬几天的夜,可是我基本没有熬过夜。不知道怎么说,我感觉自己好像什么也没学到,但是什么作业都完成了,这学期很快既要结束了,我就这样在家度过了OO,度过了大二下学期,真是有够神奇的,有点像做梦。

 

你可能感兴趣的:(OO第三单元总结)