面向对象第三单元总结

面向对象第三单元总结

一、JML相关总结

1.1理论基础

JML是Java Modeling Language的缩写,是一种行为接口规范的语言,用于指定Java模块的行为。用于指定一些必要的行为或者结果,但却并不指定具体如何实现它。

JML是写一段特殊规格的注释来指定一个模块的行为,一段JML一般有异常处理部分和正常行为部分,当然,如果没有规定异常的处理,前一部分便可以省略。

正常行为部分一般有三个内容: · requires :规定了这个规格的前置条件,即需要满足什么条件才能保证进入这个方法的数据,最终run出来的结果是正确符合预期的。

· assignable : 这一部分指明了规格的副作用范围,也就是在这个方法执行前后,有哪些变量会被改变。

· ensures : 指明了方法的后置条件,我的理解就是指明了方法运行结束后的结果应该满足的条件。

异常处理的部分大致类似,也有requires和assignable部分,但是最后一个部分可以是ensures也可以是:

· signal_only :抛出一个异常。

JML通过其它相应的语法,完成对上面各个部分的填充,就完成了对Java一个模块的规范化描述,使得懂得JML的人可以根据这段规格来实现这个模块。

1.2工具链

在JML的疑似官网上面我看到了和JML相关的若干工具: · OpenJML : 是Java程序的程序验证工具,允许检查满足JML语言规范的JML注释的程序规范。

· AspectJML : 能够指定并做运行时断言检查Java和AspectJ程序。

· Sireum / Kiasan for Java :这是基于JML规范的Java程序单元自动验证和测试用例生成工具集。

· JMLUnitNG : 用于JML注释的Java代码的自动化单元测试生成工具。

· JMLOk : 这是一种用随机测试来检查JML规范的Java代码的工具,并给出它所发现的不一致性问题的可能原因。

· AutoJML :是一种从各种格式(包括UML图)和安全协议规范的状态图生成JML规范的工具。

二、部署SMT Solver

使用IDEA扩展工具中的OpenJML来进行检查。

对第一次规格作业中的PathContainer进行检测,得到的结果如下:

F:\6\oo\homework 9\first - right\src\MyPathContainer.java:18: 警告: Method size overrides parent class methods and so its specification should begin with 'also'
  //@ ensures \result == pList.length;
      ^
F:\6\oo\homework 9\first - right\src\MyPathContainer.java:23: 警告: Method containsPath overrides parent class methods and so its specification should begin with 'also'
  /*@ requires path != null;
      ^
F:\6\oo\homework 9\first - right\src\MyPathContainer.java:36: 警告: Method containsPathId overrides parent class methods and so its specification should begin with 'also'
  /*@ ensures \result == (\exists int i; 0 <= i && i < pidList.length;
      ^
F:\6\oo\homework 9\first - right\src\MyPathContainer.java:47: 警告: Method getPathById overrides parent class methods and so its specification should begin with 'also'
  /*@ public normal_behavior
              ^
F:\6\oo\homework 9\first - right\src\MyPathContainer.java:66: 警告: A non-pure method is being called where it is not permitted: com.oocourse.specs1.models.Path.isValid()
    @ requires path != null && path.isValid() && containsPath(path);
                                            ^
F:\6\oo\homework 9\first - right\src\MyPathContainer.java:72: 警告: A non-pure method is being called where it is not permitted: com.oocourse.specs1.models.Path.isValid()
    @ signals (PathNotFoundException e) !path.isValid();
                                                      ^
F:\6\oo\homework 9\first - right\src\MyPathContainer.java:65: 警告: Method getPathId overrides parent class methods and so its specification should begin with 'also'
  /*@ public normal_behavior
              ^
F:\6\oo\homework 9\first - right\src\MyPathContainer.java:84: 警告: A non-pure method is being called where it is not permitted: com.oocourse.specs1.models.Path.isValid()
    @ requires path != null && path.isValid();
                                            ^
F:\6\oo\homework 9\first - right\src\MyPathContainer.java:96: 警告: A non-pure method is being called where it is not permitted: com.oocourse.specs1.models.Path.isValid()
    @ requires path == null || path.isValid() == false;
                                            ^
F:\6\oo\homework 9\first - right\src\MyPathContainer.java:83: 警告: Method addPath overrides parent class methods and so its specification should begin with 'also'
  /*@ normal_behavior
      ^
F:\6\oo\homework 9\first - right\src\MyPathContainer.java:114: 警告: A non-pure method is being called where it is not permitted: com.oocourse.specs1.models.Path.isValid()
    @ requires path != null && path.isValid() && \old(containsPath(path));
                                            ^
F:\6\oo\homework 9\first - right\src\MyPathContainer.java:114: 错误: A \old token with no label may not be present in a requires clause
    @ requires path != null && path.isValid() && \old(containsPath(path));
                                                      ^
F:\6\oo\homework 9\first - right\src\MyPathContainer.java:124: 警告: A non-pure method is being called where it is not permitted: com.oocourse.specs1.models.Path.isValid()
    @ signals (PathNotFoundException e) path.isValid()==false;
                                                    ^
F:\6\oo\homework 9\first - right\src\MyPathContainer.java:113: 警告: Method removePath overrides parent class methods and so its specification should begin with 'also'
  /*@ public normal_behavior
              ^
F:\6\oo\homework 9\first - right\src\MyPathContainer.java:139: 错误: A \old token with no label may not be present in a requires clause
    @ requires \old(containsPathId(pathId));
                    ^
F:\6\oo\homework 9\first - right\src\MyPathContainer.java:138: 警告: Method removePathById overrides parent class methods and so its specification should begin with 'also'
  /*@ public normal_behavior
              ^
F:\6\oo\homework 9\first - right\src\MyPathContainer.java:160: 警告: A non-pure method is being called where it is not permitted: com.oocourse.specs1.models.Path.containsNode(int)
@           (\forall int i; 0 <= i && i < arr.length; (\exists Path p; this.containsPath(p); p.containsNode(arr[i])))
                                                                                                            ^
F:\6\oo\homework 9\first - right\src\MyPathContainer.java:161: 警告: A non-pure method is being called where it is not permitted: com.oocourse.specs1.models.Path.containsNode(int)
@           &&(\forall Path p; this.containsPath(p); (\forall int node; p.containsNode(node); (\exists int i; 0 <= i && i < arr.length; node == arr[i])))
                                                                                        ^
F:\6\oo\homework 9\first - right\src\MyPathContainer.java:159: 警告: Method getDistinctNodeCount overrides parent class methods and so its specification should begin with 'also'
  /*@ ensures (\exists int[] arr; (\forall int i, j; 0 <= i && i < j && j < arr.length; arr[i] != arr[j]);
      ^
2 个错误
17 个警告

给出了两个错误和十七个警告。两个错误说的似乎都是\old,翻译过来似乎是old不能够出现在requires语句当中。其余的警告主要是两类,一类是重写父类的方法,规格应该以also开头,另外一类是调用了非纯的并且未经允许的方法。

三、部署JMLUnitGN/JMLUnit

针对简单的比较函数进行检测:

/*@ public normal_behaviour
  @ ensures \result == lhs - rhs;
*/
public static int compare(int lhs, int rhs) {
  return lhs - rhs;
}

用相关指令序列进行JMLUnitGN的使用

F:\6\oo\blog\third>java -jar F:\6\oo\share\jmlunitng.jar -d test -sp src src\Demo.java

F:\6\oo\blog\third>javac -cp "F:\6\oo\share\jmlunitng.jar" -sourcepath src -d testout test\*.java

F:\6\oo\blog\third>java -jar F:\6\oo\share\openjml\openjml.jar -rac -specspath src src\Demo.java

F:\6\oo\blog\third>java -cp "F:\6\oo\share\jmlunitng.jar;./" testout\Demo_JML_Test
错误: 找不到或无法加载主类 testout\Demo_JML_Test

F:\6\oo\blog\third>cd testout

F:\6\oo\blog\third\testout>java -cp "F:\6\oo\share\jmlunitng.jar;./" Demo_JML_Test

运行的结果是:

F:\6\oo\blog\third\testout>java -cp "F:\6\oo\share\jmlunitng.jar;./" Demo_JML_Test
[TestNG] Running:
Command line suite

Failed: racEnabled()
Passed: constructor Demo()
Passed: static compare(-2147483648, -2147483648)
Passed: static compare(0, -2147483648)
Passed: static compare(2147483647, -2147483648)
Passed: static compare(-2147483648, 0)
Passed: static compare(0, 0)
Passed: static compare(2147483647, 0)
Passed: static compare(-2147483648, 2147483647)
Passed: static compare(0, 2147483647)
Passed: static compare(2147483647, 2147483647)
Passed: static main(null)
Passed: static main({})

===============================================
Command line suite
Total tests run: 13, Failures: 1, Skips: 0
===============================================

只有一个racEnabled()Failed了,其余的从最后的结果看起来,似乎实在进行一些边界情况的检测。

Demo_JML_Test中racEnabled()是这样的:

@Test
public void test_racEnabled() {
 Utils.useExceptions = true;
 Assert.assertFalse
(Utils.isRACCompiled(Demo_JML_Test.class),
  "JMLUnitNG tests must not be RAC-compiled when using OpenJML RAC.");
 Assert.assertTrue
(Utils.isRACCompiled(Demo.class),
  "JMLUnitNG tests can only run on RAC-compiled code.");
}

似乎是通过isRACCompiled这个方法来判断到底是不是RACCompiled的,然后其余的test前面有一句:

@Test(dependsOnMethods = { "test_racEnabled" }, 

应该是test_racEnabled()这个是false的时候,其余的test就不进行检测。那大概意思就是racEnabled不成立的时候,会跳过后面的所有test,而这个本身是运行时检测,具体为什么failed我好像没有找到原因。

四、架构设计

4.1第九次作业

第一次作业比较简单,在Path中使用一个Arraylist来存储结点。在PathContainer中使用了三个hashmap来存储信息,一个存path和id的对应关系,一个存id和path的对应关系,还有一个存节点和节点统计的hashmap。其余没有什么特别的。其中用两个hashmap来存储id和path的对应关系只是为了让查找的时候时间更短而已。

4.2第十次作业

第二次作业单独开了一个叫做AdjMat类,用了两个hashmap嵌套来存储相关内容,一个存储邻接矩阵,一个存储点到点的距离。

private HashMap<Integer, HashMap<Integer, Integer>> adjMat =
      new HashMap<>();
private HashMap<Integer, HashMap<Integer, Integer>> length =
      new HashMap<>();

然后通过bfs来计算最短的距离,然后存进length当中。

4.3第十一次作业

第三次作业其实就是处理带权图的问题,不满意度,价钱,换乘三个问题都是算带权图最短路径的问题,只是带权的方式不同而已。对于这三个问题,使用了一个叫做WeightedGraph的类,完善了带权图的相关内容,主要是维护带权图和进行迪杰斯特拉算法存储距离信息。然后另外两个类作为子类继承,重写了赋权的部分,这样就节省了大量的重复代码。至于NodeTuple是一个二元组,是存了节点和节点属于的path。

第三次作业计算block的问题,采用了并查集的相关算法,所以单独造了一个叫做block的类。

五、bug分析和修复

bug1:block当中用于存储属于哪一个block的数据没有清洗。在计算block的时候,采用的是并查集的方法, 并用一个hashmap结构来存储,key为node值,value为node所属的block。但是每次进行path_add或者remove之后,需要清空hashmap然后重新计算。不清空的时候,会导致使用脏数据,而产生错误。在每次重新进行并查集的计算时候,加入clear操作,便可以了。

修复情况:

 

 

修复前,输出三条路径,以及计算结果为错误的1.

修复后,输出的三条路径,以及正确结果2.

bug2:equals错误,在自建结点二元组NodeTuple的时候,作为hashkey需要写equals方法,而equals需要比较节点值和path的值,但是我比较path的时候,用了"=="比较而不是用equals方法比较,从而导致了错误的产生,导致无法删除的故障,这就和后面实验中给的错误是一样的。

修复后的equals,途中第六行的path.equals原来为==:

修复效果:

 

六、心得体会

这部分其实是分两步的,一方面是写规格,一方面是根据规格写代码。

写规格的时候需要考虑完善,需要仔细思考慢慢琢磨。

而根据规格写代码似乎工作量要比前者大很多,不仅要考虑如何实现,还得考虑实现的要符合规格。实现的部分需要很仔细的保证没有问题。但同时规格规定的并不一定是实现的,比如这次作业中规定path中应该用数组存,但实际上用Arrayllist存储会更加方便。规格简化了很多东西,这给实现带来了很大的发挥空间。

posted on 2019-05-22 20:00  没心的先生懒  阅读(193)  评论(0编辑  收藏  举报