目录

  • BUAA_OO_JML单元总结

    • 一、JML语言理论基础

      • JML概述
      • JML语法简述
      • JML应用工具链
    • 二、SMT Solver应用
    • 三、JML UnitNG自动生成样例
    • 四、本单元作业分析
      • 架构设计与重构分析
      • BUG分析与修复
    • 五、单元学习体会

BUAA_OO_JML单元总结

一、JML语言理论基础

JML概述

Java建模语言(JML)是一种行为接口规范语言,可用于指定Java模块的行为。我们知道,面向对象分析和设计的原则之一就是应当尽可能地把过程设想往后推,把精力集中于整体架构的设计,协调管理各个单元部件的运转,而不拘泥于某些琐碎细节实现当中。

因此,在设计层面,我们对于每一个类以及方法,要淡化过程,只需关注它的结果,所以我们需要精准地描述出它的行为以及影响。(就和计组中,我们使用列表的方法将IFU、ALU、GRF等部件的输入输出功能全部列举出来一样。)但是 Java 语言没有提供显式地将这种信息合并到代码中的方法,JML便应运而生。

JML语法简述

JML作为一种接口规格语言,包含的大量知识是一篇博客所不能承载的。

首先,如果有什么记不清的内容,当然首选查阅课程组JML手册(链接在这里)啦。

其次,官方文档(链接在这里)中包含了JML的全部内容,想要拓展JML语法知识可以选择食用。

按惯例列举一波常用操作:

1、JML表达式

  • 原子表达式
    \result\old()\not_assigned()\not_modified()\nonnullelements()\type()\typeof()
  • 量化表达式
  \forall\exists\sum\product\max\min\num_of
  • 操作符

  子类型关系操作符 <:

  等价关系操作符 <==>

  推理操作符 ==>

  变量应用 \nothing \everything

2、方法规格

  • 前置条件 reqquires
  • 后置条件 ensures
  • 副作用范围限定 assignable modifiable
  • signals子句

3、类规格

  • 状态变化约束 constraint
  • 不变式 invariant

如果发现自己不记得,当然要回去再看看啦~

JML应用工具链

在本单元学习JML的过程中主要引用到的工具有SMT Solver、OpenJML、JML UnitNG。吐槽一下这些工具的帮助文档写的比较魔鬼以及中文资料真的非常匮乏,所以真心感谢讨论区大佬们的帮助!

二、SMT Solver应用

这里使用的是OpenJML命令行来进行代码审查,配置过程参考讨论区伦佬帖子。

同时为了方便命令行表述,可以编写bat来进行简化(或者bash)。这里使用bat,保存为openjml.bat并添加到环境变量中。

java -jar D:/OpenJML/openjml-0.8.42-20190401/openjml.jar %*

测试一 使用check选项检查JML语法

只需使用

openjml -check xxxxx.java

检查相应的文件即可,如果所有JML语法正确,那么将不会有任何显示,如果有错误,那么将会出现提示

而这个功能局限性也非常大,首先不能检查/forall、/exists等语句,会报错;其次,也不能检测到继承。

这里以Graph.java作为例子,检查结果如下(只贴部分):

Graph.java:6: 错误: 找不到符号
public interface Graph extends PathContainer {^符号: 类 PathContainer
Graph.java:28: 错误: 找不到符号public /*@pure@*/ boolean isConnected(int fromNodeId, int toNodeId) throws NodeIdNotFoundException;^符号:   类 NodeIdNotFoundException位置: 接口 Graph
Graph.java:47: 错误: 找不到符号public /*@pure@*/ int getShortestPathLength(int fromNodeId, int toNodeId) throws NodeIdNotFoundException, NodeNotConnectedException;^符号:   类 NodeIdNotFoundException位置: 接口 Graph
Graph.java:47: 错误: 找不到符号public /*@pure@*/ int getShortestPathLength(int fromNodeId, int toNodeId) throws NodeIdNotFoundException, NodeNotConnectedException;^符号:   类 NodeNotConnectedException位置: 接口 Graph
Graph.java:7: 错误: 找不到符号//@ ensures \result == (\exists Path path; path.isValid() && containsPath(path); path.containsNode(nodeId));^符号:   类 Path位置: 接口 Graph
Graph.java:10: 错误: 找不到符号/*@ ensures \result == (\exists Path path; path.isValid() && containsPath(path);^符号:   类 Path位置: 接口 Graph
Graph.java:17: 错误: 找不到符号@ requires (\exists Path path; path.isValid() && containsPath(path); path.containsNode(fromNodeId)) &&^符号:   类 Path位置: 接口 Graph
Graph.java:18: 错误: 找不到符号@          (\exists Path path; path.isValid() && containsPath(path); path.containsNode(toNodeId));^符号:   类 Path位置: 接口 Graph
Graph.java:25: 错误: 找不到符号@ signals (NodeIdNotFoundException e) (\forall Path path; containsPath(path); !path.containsNode(fromNodeId));^符号:   类 NodeIdNotFoundException位置: 接口 Graph
Graph.java:25: 错误: 找不到符号@ signals (NodeIdNotFoundException e) (\forall Path path; containsPath(path); !path.containsNode(fromNodeId));^符号:   类 Path位置: 接口 Graph
Graph.java:26: 错误: 找不到符号@ signals (NodeIdNotFoundException e) (\forall Path path; containsPath(path); !path.containsNode(toNodeId));^符号:   类 NodeIdNotFoundException位置: 接口 Graph
Graph.java:26: 错误: 找不到符号@ signals (NodeIdNotFoundException e) (\forall Path path; containsPath(path); !path.containsNode(toNodeId));^符号:   类 Path位置: 接口 Graph
Graph.java:31: 错误: 找不到符号@ requires (\exists Path path; path.isValid() && containsPath(path); path.containsNode(fromNodeId)) &&

所以这个功能,玩玩开心就好

测试二 使用esc选项进行代码静态检查

静态检查不依赖于JML。使用

openjml -esc xxxxx.java

即可检查。

测试三 使用rac选项进行动态检查

使用指令

java -jar openjml.jar -rac xxxx.java
java -classpath ".;jmlruntime.jar" xxxx

即可检查错误,这里自己写一个测试程序,在JML中故意写错

public class Add {//@ requires a > 0;//@ requires b > 0;//@ ensures \result == a-b;public static int add(int a, int b) {return a + b;}public static void main(String args[]) {System.out.println(add(2, 3));}
}

可以看到以及发现了我的错误——把加写错成了减。

三、JML UnitNG自动生成样例

OpenJML中带有jmluniting.jar文件,即可让我们使用JML UnitNG功能来进行自动生成样例测试。

使用

java -jar jmlunitng.jar xxxxx.java
javac -cp jmlunitng.jar *.java
java -jar openjml.jar -rac xxxx.java
java -cp jmlunitng.jar xxxx.Compare_JML_Test

即可得到相应的结果

这里自己新建一个简单的加法函数

public class Add {/*@ requires a > 0;@ requires b > 0;@ ensures \result == a+b;@*/public static int add(int a, int b) {return a + b;}public static void main(String args[]) {System.out.println(add(1, 2));}
}

先用

java -jar jmlunitng.jar Add.java

得到以上文件,

接着用

javac -cp .\jmlunitng.jar *.java

编译出相应的class

使用

java -jar openjml.jar -rac Add.java

就能得到相应的JML_TEST程序文件

最后运行即可看到自动生成的样例以及给出的结果

原因显而易见,两个极限int数相加会造成int溢出。

四、本单元作业分析

架构设计与重构分析

第一次作业

第一次作业我们实现的只是简单的路径和容器,功能需求可以说非常简单。但是在复杂度方面我们还是需要权衡,三次作业都是如此。在第一次作业中我也有想到后面的发展走向,所以使用了多个Hashmap来优化查找操作,建立了
$$
边号<->次数、点号<->次数、路径<->路径编号
$$
之类的映射关系。将主要的复杂度集中在图结构变更指令中,每次变更图结构的同时刷新Hashmap。这样的架构在之后的两次作业都能得到沿用,不需要进行重构,只在必要时进行小范围修补即可。

  • 类协作关系

  • 方法复杂度统计

第二次作业

第二次作业相比于第一次作业增加了若干功能函数,新增了“边”的概念,同时引入连通性问题和最短路问题,在第一次代码的基础上,我只需要新增求解最短路问题的方法类即可。同时由于我们的点号是随机的,为了便于使用最短路算法,我增加了一个映射Hashmap,将所有点的点号按自然数从低到高排列映射。

在算法方面,我使用了Dijkstra算法,并使用了优先队列优化,相对于Floyed较高的复杂度,我在第三次作业中仍然可以直接沿用前面的算法,无需重构,没有爆TLE的风险。

  • 类协作关系

  • 方法复杂度统计

第三次作业

第三次作业在第二次作业上继续进行了功能性的扩充,增加了连通块以及最小换乘、最少不满意度、最低票价等等功能。但是除了连通块,其他几个功能本质上仍然是图的最短路的问题。

在处理连通块问题,这里使用了从网上学习到的非常巧妙的并查集思路。非常推荐这篇博客,同时也为自己的弱鸡算法能力感到羞愧。

最小换乘、最少不满意度、最低票价其实都是最短路的变种问题,几种问题都只需要在建立图的策略上下功夫,如果最短路算法复杂度不爆,完全可以不进行修改。这里使用的也是大家常用的拆点做法,给换乘车站在每条Path上都设一个“站台”。详细的过程可以参考zyy在讨论区里面的方法,我们没必要完全按照这个思路拆点,只需要理解拆点这个过程就可以自己设计代码。

  • 类协作关系

  • 方法复杂度统计
    method ev(G) iv(G) v(G)
    ConnectedBlocks.add(int,int) 1.0 1.0 1.0
    ConnectedBlocks.ConnectedBlocks(int) 1.0 1.0 2.0
    ConnectedBlocks.Find(int) 1.0 2.0 2.0
    ConnectedBlocks.getConnectedBlockCount() 1.0 1.0 1.0
    ConnectedBlocks.join(int,int) 1.0 1.0 2.0
    ConnectedBlocks.work() 1.0 1.0 3.0
    Dijkstra.addEdge(int,int,int) 1.0 1.0 1.0
    Dijkstra.Dijkstra() 1.0 2.0 2.0
    Dijkstra.dijkstra(int) 4.0 4.0 8.0
    Dijkstra.removeEdge(int,int) 5.0 5.0 5.0
    Dijkstra.work(int,int) 1.0 1.0 1.0
    Edge.compareTo(Edge) 1.0 1.0 1.0
    Edge.Edge(int,int) 1.0 1.0 1.0
    Edge.getCost() 1.0 1.0 1.0
    Edge.getTo() 1.0 1.0 1.0
    Main.main(String[]) 1.0 1.0 1.0
    MyGraph.addPath(Path) 2.0 9.0 10.0
    MyGraph.containsEdge(int,int) 2.0 2.0 3.0
    MyGraph.containsNode(int) 1.0 1.0 1.0
    MyGraph.containsPath(Path) 3.0 2.0 3.0
    MyGraph.containsPathId(int) 1.0 1.0 1.0
    MyGraph.flushDataAdd(String,int) 1.0 9.0 9.0
    MyGraph.flushDataSub(String,int) 1.0 3.0 3.0
    MyGraph.getChangeStation() 1.0 1.0 1.0
    MyGraph.getConnectedBlocks() 1.0 1.0 1.0
    MyGraph.getDistinctNodeCount() 1.0 1.0 1.0
    MyGraph.getLeastChange() 1.0 1.0 1.0
    MyGraph.getLeastPrice() 1.0 1.0 1.0
    MyGraph.getLeastUnpleasant() 1.0 1.0 1.0
    MyGraph.getPathById(int) 3.0 3.0 3.0
    MyGraph.getPathId(Path) 4.0 4.0 5.0
    MyGraph.getShortestPathLength(int,int) 5.0 1.0 5.0
    MyGraph.getTransMap() 1.0 1.0 1.0
    MyGraph.isConnected(int,int) 5.0 1.0 5.0
    MyGraph.MyGraph() 1.0 1.0 1.0
    MyGraph.rebuildDij() 1.0 2.0 2.0
    MyGraph.rebuildSecondMap() 1.0 10.0 10.0
    MyGraph.removePath(Path) 4.0 10.0 11.0
    MyGraph.removePathById(int) 3.0 9.0 9.0
    MyGraph.size() 1.0 1.0 1.0
    MyPath.compareTo(Path) 6.0 3.0 7.0
    MyPath.containsNode(int) 1.0 1.0 1.0
    MyPath.getChangePathMap() 1.0 1.0 1.0
    MyPath.getDistinctNodeCount() 1.0 1.0 1.0
    MyPath.getEdgeMap() 1.0 1.0 1.0
    MyPath.getList() 1.0 1.0 1.0
    MyPath.getMap() 1.0 1.0 1.0
    MyPath.getNode(int) 1.0 1.0 1.0
    MyPath.getPathString() 1.0 1.0 1.0
    MyPath.getUnpleasantValue(int) 2.0 1.0 2.0
    MyPath.isValid() 1.0 1.0 1.0
    MyPath.iterator() 1.0 1.0 1.0
    MyPath.MyPath(int[]) 1.0 7.0 9.0
    MyPath.size() 1.0 1.0 1.0
    MyRailwaySystem.getConnectedBlockCount() 1.0 1.0 2.0
    MyRailwaySystem.getLeastTicketPrice(int,int) 5.0 1.0 7.0
    MyRailwaySystem.getLeastTransferCount(int,int) 5.0 1.0 5.0
    MyRailwaySystem.getLeastUnpleasantValue(int,int) 5.0 1.0 7.0
    MyRailwaySystem.getUnpleasantValue(Path,int,int) 1.0 1.0 1.0
    Total 106.0 128.0 173.0
    Average 1.7966101694915255 2.169491525423729 2.9322033898305087

在这次作业中由于在图结构变更函数"add"、"remove"中包含了比较多的刷新Hashmap、建立图等操作,所以方法复杂度较高。

BUG分析与修复

第一次作业

第一次作业出现的bug非常非常非常傻,问题出在compareTo函数中,在比较字典序的过程中我选择直接拼接字符串来使用字符串的compare方法进行比较。导致强测被炸了五六个点,后面老老实实一个一个节点比较就没问题了 。(枯了555

第二次作业

第二次作业通过了所有中测以及强测测试点,互测中没有出现bug。

第三次作业

第三次作业通过了所有中测以及强测测试点。

但是在互测中被狼?人用50组ADD+120个节点+每个节点都有几十次换乘爆了数组越界错误。原因是在拆点时候我只开了4000个节点。

(大意了,心里想着50*80=4000就足够了)

被hack扣一分无伤大雅,但是以后还是要警醒,要么开大一点点数组,要么用可变长度。

五、单元学习体会

本单元任务相对于前两个单元较为轻松,除了引入了JML规格,在java实际的编码过程中其实都在做一个以前知识点的回顾。课下的三次作业,其实在脱离JML的情况下也可以正确的完成。我在阅读接口的JML遇到疑惑时,问我身边的很多同学,他们很多都表示压根没看JML直接开始编码。所以我觉得OO的JML系列作业可以设置更多坑点来促进我们在编码过程中结合JML思考问题。这样更能够让我们体会到JML契约式编程的好处。

所以我感觉相对于这几次作业,课上实验撰写JML规格也让人印象深刻。新入坑JML,感觉JML很多时候写起来难度比正式编码还要大。而且从搜索引擎查找到的资料来看,JML的中文资料是非常匮乏的,我们还需要勤于收集资料才能进一步加深对JML的学习。

另外这单元的三次作业仍然是递增迭代的过程,这也是每个单元作业的共同特征。相较于以前单元的次次作业重构,现在写作业已经可以偷不少懒了,感谢OO这学期给我们带来的进步。

还剩最后一个单元,我们一起征服。

转载于:https://www.cnblogs.com/NoobKingCWR/p/10908619.html

BUAA_OO_JML单元总结相关推荐

  1. 《静态时序分析实用方法》翻译

    第1章 引言 解释了什么是静态时序分析以及它如何用于时序验证.还描述了功率和可靠性方面的考虑. 概述了纳米设计的静态时序分析程序.本章解决了诸如什么是静态时序分析.噪声和串扰的影响是什么.如何使用这些 ...

  2. Excel如何设置单元格行高,办公入门

    在使用Excel做设计文档时,遇到一个问题,一组报文放入一个单元格,但因为只显示一行,我的信息就成了下面这个样子

  3. php多表存储,php – MySql在一个单元格中存储另一个表的多个引用并选择它?

    我有两张桌子 table: people id name goods_owned 1 john 1,4,3 2 Mike 2,5 3 Sam 1,5,2 4 Andy 5,3,4 – table go ...

  4. LeetCode简单题之Excel 表中某个范围内的单元格

    题目 Excel 表中的一个单元格 (r, c) 会以字符串 "" 的形式进行表示,其中: 即单元格的列号 c .用英文字母表中的 字母 标识. 例如,第 1 列用 'A' 表示, ...

  5. LeetCode简单题之距离顺序排列矩阵单元格

    题目 给定四个整数 row , cols , rCenter 和 cCenter .有一个 rows x cols 的矩阵,你在单元格上的坐标是 (rCenter, cCenter) . 返回矩阵中的 ...

  6. java jtable 单元格合并_JTable 单元格合并 【转】

    最近,我为了做一个管理系统,需要用到合并JTable的单元格.查找了很多资料,终于简单的实现了.现在把代码共享出来,希望对大家有用. 本程序主要实现行的合并,列的合并大家可以根据下面的代码修改. CM ...

  7. python 2x xlrd使用merged_cells 读取的合并单元格为空

    一个简单的excel 如下 代码如下 #!/usr/bin/python# # -*- coding: utf-8 -*- import xlrd import sys reload(sys) sys ...

  8. 如何设置REUSE_ALV_GRID_DISPLAY'的单个单元格的是否可以输入

    代码如下:具体说明参见红色说明(本例子是从订单明细提取两个字段的数据到内表) REPORT ZALV_EDIT. TYPE-POOLS: SLIS. *- Fieldcatalog DATA: IT_ ...

  9. ios 如何在cell中去掉_IOS之表视图单元格删除、移动及插入

    1.实现单元格的删除,实现效果如下 - (void)viewDidLoad { [super viewDidLoad]; //设置导航栏 self.editButtonItem.title = @&q ...

最新文章

  1. 联想笔记本V470安装Win8.1 X64位系统,关机黑屏、电源灯亮
  2. 水果电池打造柠檬电动汽车!
  3. python-mysql驱动64位
  4. Leetcode--300. 最长上升子序列
  5. 优秀的产品经理和一般的产品经理之间的区别
  6. php中的Register Globals
  7. LeetCode:Rotate Image
  8. jdk1.8 ConcurrentHashMap
  9. thinkpython2e电子书下载_《Think Python 2e》中译版电子书分享
  10. 犹太教、基督教和伊斯兰教的简单关系
  11. 面向对象基础知识四:关联关系
  12. python快乐数,快乐数 - SegmentFault 思否
  13. python vim插件_有哪些好用到爆的vim插件?
  14. 岁末年初,P2P网贷理财正当时
  15. 零基础数学建模学习日记Day1
  16. Data Structure - Doubly Linked List (Java)
  17. 乘风破浪的技术大咖再次集结 | 腾讯云TVP持续航行中
  18. java接口面试题_7道经典Java接口面试题
  19. python中完整爬取股票财务信息和公司基本信息含xpath
  20. fsl左右海马体分割+freesurfer合并

热门文章

  1. Linux基础篇四———管道命令
  2. 微信公众号如何关联小程序(小程序如何开发)
  3. AR/VR难改歌尔股份代工命
  4. 厉害了!公诚咨询自主研发的“5G+”切片式监理产品,助力全国首个5G SA独立组网试验网开通...
  5. 新生研讨课课后感ldy
  6. 什么是Windows服务
  7. 写给那些拼命工作的程序员
  8. 腾讯AI Lab 崔乐阳博士忆西湖大学读博期间和张岳导师之间的故事
  9. Python 生成8位必含数字、大小写字母的字符串(密码)
  10. 2022-07-01:某公司年会上,大家要玩一食发奖金游戏,一共有n个员工, 每个员工都有建设积分和捣乱积分, 他们需要排成一队,在队伍最前面的一定是老板,老板也有建设积分和捣乱积分, 排好队后,所有