【IT168 技术文章】面向对象分析和设计的原则之一就是应当尽可能地把过程设想往后推。我们大多数人只在实现方法之前遵守这一规则。一旦确定了类及其接口并该开始实现方法时,我们就转向了过程设想。那么到底有没有别的选择?和大多数语言一样,编写 Java 代码时,我们需要为计算每个方法的结果一步一步地提供过程。

就其本身而言,过程化表示法只是说 如何做某事,却不曾说过我们正在设法做 什么。在动手之前了解我们想要取得的结果,这可能会有用,但是 Java 语言没有提供显式地将这种信息合并到代码中的方法。

Java 建模语言(JML)将注释添加到 Java 代码中,这样我们就可以确定方法所执行的内容,而不必说明它们如何做到这一点。有了 JML,我们就可以描述方法预期的功能,无需考虑实现。通过这种方法,JML 将延迟过程设想的面向对象原则扩展到了方法设计阶段。

JML 为说明性的描述行为引入了许多构造。这些构造包括模型字段、量词、断言的可见度范围、前提条件、后置条件、不变量、合同继承以及正常行为与异常行为的规范。这些构造使得 JML 的功能变得非常强大,但是没必要理解或使用所有这些构造,也没必要马上使用所有的构造。您可以从简单的起点开始逐步学习和使用 JML。

在本文中,我们将采取循序渐进的方法来学习 JML。我们将从概述使用 JML 的好处入手,重点讲述它对开发和编译过程的影响。接下来,我们将讨论用于前提条件、后置条件、模型字段、量化、副作用和异常行为的 JML 构造。在这个过程中,示例会显示每种 JML 构造的动机。在本文结尾处,您将对 JML 的工作原理有个概念性的理解,并能将它应用到您自己的程序中。

JML 概述

使用 JML 来说明性地描述所希望的类和方法的行为,可以显著地改善整个开发过程。将建模表示法添加到 Java 代码中,其好处包括以下几点:

能更加精确地描述代码所完成的任务

能有效地发现和纠正错误

能减少随着应用程序的进展而引入错误的机会

能较早地发现客户没有正确使用类

能产生始终与应用程序代码保持同步的精确文档

JML 注释始终位于 Java 注解(comment)内部,因此它们不会对进行正常编译的代码产生影响。当我们想将类的实际行为与其 JML 规范进行比较时,可以使用开放源码 JML 编译器。用 JML 编译器编译过的代码如果没有做到规范中规定它应该做的事,那么该代码在运行时会抛出 JML 异常。这不仅能捕获代码中的错误,还能确保文档(JML 注释格式)与代码保持同步。

在以下几节中,我将使用开放源码 Jakarta 公共集合组件(Jakarta Commons Collection Component,JCCC)的 PriorityQueue 接口和 BinaryHeap 类来说明 JML 的特性。

需求和职责

本文的源代码包括了 JCCC 的 PriorityQueue 接口。 PriorityQueue 接口包含了方法特征符,这些特征符指定了参数和返回值的数据类型,但是没有说明任何有关方法行为的内容。我们希望能指定 PriorityQueue 的语义,这样的话实现它的所有类就能以预期的方式运作(在没有行为规范的情况下,我们最终会得到一个实现了 PriorityQueue 接口的堆栈类,或者其它某种非常规的情况)。

请研究 PriorityQueue 中 pop() 方法。优先级队列对 pop() 有什么需求呢?有三个基本需求。首先,除非队列中至少有一个元素,否则不应该调用 pop() 。其次,它应当返回队列中优先级最高的元素。最后,它应当除去从队列中返回的元素。

清单 1 演示了表示第一个需求的 JML 注释:

清单 1. pop() 方法需求的 JML 注释

1 /*@2 3 @ public normal_behavior4 5 @ requires ! isEmpty();6 7 @*/8 9 Object pop()throwsNoSuchElementException;10

如前面所述,JML 注释是写在 Java 注解内部的。包含 JML 的多行注解是以字符 /*@ 开头的。JML 忽略行中所有作为第一个非空格字符的 @ 符号。JML 还可以写在以 //@ 开头的单行注解内部。

这里 JML 中 public 关键字的意义和在 Java 语言中的意义是一样的。 public 表示该 JML 规范对于应用程序中的其它所有类都是可见的。公共规范只能涉及公共方法和成员变量。JML 还允许规范拥有 private 、 protected 或 package 级别的作用域。JML 的作用域规则与 Java 语言中的相应规则类似。

normal_behavior 关键字表示该规范描述了 pop() 正常返回而没有抛出异常的情况。随后我们将讨论如何确定异常行为。

前提条件和后置条件

JML 关键字 requires 用于前提条件。 前提条件(precondition)指的是在调用方法之前必须要满足的条件。清单 1 指出 pop() 的前提条件是 isEmpty() 返回 false ;也就是说,队列中至少要包含一项。

方法的 后置条件(postcondition)指定该方法的职责;也就是说,当方法返回时,后置条件应当是 true。在我们的示例中, pop() 应当返回队列中拥有最高优先级的元素。我们希望指定该后置条件,这样 JML 就可以在运行时检查它。要做到这一点,我们需要清楚已经添加到优先级队列中的所有值,以便确定 pop() 应当返回哪一个值。我们可以考虑将一个成员变量添加到 PriorityQueue 以便在队列中存储这些值,但是这里有两个问题:

PriorityQueue 是一个接口,因此它应当与不同的实现(比如二进制堆、Fibonacci 堆或日历队列)兼容。 PriorityQueue 的 JML 注释不应当描述任何有关实现的内容。

作为接口, PriorityQueue 只能包含静态成员变量。

JML 用 模型字段的概念解决了这一两难局面。

模型字段

模型字段类似于只能在行为规范中使用的成员变量。下面有一个在 PriorityQueue 中使用的模型字段声明示例:

//@ public model instance JMLObjectBag elementsInQueue;

该声明指出有一个模型字段 elementsInQueue ,其数据类型为 JMLObjectBag (属于 JML 的 bag 数据类型)。 instance 关键字表示:即使该字段是在接口中进行声明的,也要在实现 PriorityQueue 的类的每个实例中分别放置该字段的非静态副本。和所有 JML 注释一样, elementsInQueue 的声明出现在 Java 注解中,因此 elementsInQueue 不能用于正规的 Java 代码中。没有哪个对象会在运行时包含 elementsInQueue 字段。

elementsInQueue 包含了添加到优先级队列的值。清单 2 显示了 pop() 的规范是如何利用 elementsInQueue 的:

清单 2. pop() 的后置条件中所使用的模型字段

1 /*@2 3 @ public normal_behavior4 5 @ requires ! isEmpty();6 7 @ ensures8 9 @ elementsInQueue.equals(((JMLObjectBag)10 11 @ \old(elementsInQueue))12 13 @ .remove(\result)) &&14 15 @ \result.equals(\old(peek()));16 17 @*/18 19 Object pop()throwsNoSuchElementException;20 21

ensures 关键字指出其后跟的是 pop() 返回时必须满足的后置条件。 \result 是 JML 关键字,它等于 pop() 所返回的值。 \old() 是 JML 函数,在调用 pop() 之前它返回其参数所具有的值。

ensures 子句包含两个后置条件。第一个指出将 pop() 所返回的值从 elementsInQueue 中除去。第二个指出返回值和 peek() 返回的值一样。

类级不变量

我们已经看到 JML 允许我们指定方法的前提条件和后置条件。它还允许我们指定类级不变量。类级不变量是这样的条件:即在类中每个方法的入口和出口处这些条件都必须为 true。例如, //@ public instance invariant elementsInQueue != null; 是 PriorityQueue 的不变量,这就是说,在实现 PriorityQueue 的类的构造函数返回之后, elementsInQueue 无论何时都不能是 null。

java jml_JML 入门相关推荐

  1. java从入门到精通_想要开始学java?你要的java从入门到精通布列如下!

    java从入门到精通,让我来告诉你! 毫无疑问,java是当下最火的编程语言之一.对于许多未曾涉足计算机编程的领域「小白」来说,深入地掌握java看似是一件十分困难的事.其实,只要掌握了科学的学习方法 ...

  2. C功底挑战Java菜鸟入门概念干货(一)

    一.认识Java 1.Java 程序比较特殊,它必须先经过编译,然后再利用解释的方式来运行.  2.Byte-codes 最大的好处是--可越平台运行,可让"一次编写,处处运行"成 ...

  3. java基础入门传智播客 源码_Java-_2020年版Java零基础视频教程(Java 0基础,Java初学入门)魔鬼讲师老杜出品...

    不会闲聊!!!不会扯淡!!!小UP只会分享与Java相关的学习资源 还记得那年带你Java入门的一声"吼"吗? B站目前播放量已经快到450多万播放量的Java零基础教程的创作者& ...

  4. 叮!您收到一份超值Java基础入门资料!

    摘要:Java语言有什么特点?如何最大效率的学习?深浅拷贝到底有何区别?阿里巴巴高级开发工程师为大家带来Java系统解读,带你掌握Java技术要领,突破重点难点,入门面向对象编程,以详细示例带领大家J ...

  5. java基础入门课后习题_《Java基础入门》课后习题及答案

    <Java基础入门>课后习题及答案Java基础入门,课后习题,答案 博学谷--让IT教学更简单,让IT学习更有效 <Java基础入门>课后习题 第1章Java开发入门 一.填空 ...

  6. Java从入门到精通08-二进制、位运算、移位运算

    Java从入门到精通08-二进制.位运算.移位运算 二进制(Binary)数用0和1两个数字及其组合来表示任何数.进位规则是"逢2进1",数字1在不同的位上代表不同的值,按从右到左 ...

  7. java技术入门培训_入门java怎么自学?推荐谁的课程?

    想要知道如何学习Java首先需要对她有一个大致的了解,Java是面向对象的编程语言,具有简单性.分布式.安全性.平台独立与可移植性.多线程.动态性等特点.现在多用于编写桌面应用程序.Web应用程序.分 ...

  8. 小白学编程“Java小白”入门解疑大全

    成功的人分几种,有一种人叫做关系户,他们渠道多,广织关系网,有一种叫做平台户,他们平台有多硬,他们就有多硬,但无论是关系户还是平台户,依靠的总是别人的手.别人的嘴巴.别人辛苦创立下来的资源,归根到底, ...

  9. B站百万播放量Java零基础教程视频(适合Java 0基础,Java初学入门),史上最细Java零基础教学视频

    是否还在为学习Java如何入门而烦恼? 是否还在为Java软件如何安装而烦恼? 是否还在找寻着适合零基础的Java视频教程? 动力节点教学总监老杜159集课堂实录Java零基础视频教程,从初学者角度出 ...

最新文章

  1. 核心算法缺位,人工智能发展面临“卡脖子”窘境
  2. [SOJ]连通性问题
  3. Spring之泛型依赖注入
  4. 关于数据仓库 — 总体工具介绍
  5. linux rawsocket java_linux下RAW SOCKET--转
  6. Python学习笔记:利用timeit计算函数调用耗时
  7. matlab循环码差错图样,基于循环码的差错控制编码建模与仿真1.doc
  8. JSK-58 合法回文【字符串】
  9. Go语言实现并行分段求和计算
  10. 07年博客迁移:心情记事
  11. svn linux客户端使用教程,linux svn 客户端安装配置
  12. 易语言-万挂作坊4.X下载,有图有真相
  13. linux怎样使用小米线刷工具,在linux上怎么样线刷小米手机
  14. 图像复原matlab论文,基于matlab图像复原论文
  15. java excel 打勾,两种方法教你如何在excel文件中打勾
  16. 【小家java】JUC并发编程之:虚假唤醒(spurious wakeup)以及推荐的解决方案
  17. kinect游戏linux,两台kinect同时运行
  18. 2022年Work-Life Balance能实现吗?
  19. iOS开发-集成阿里云实人认证
  20. linux的各种自带库-lz -lrt -lm -lc都是什么库

热门文章

  1. python3.6+selenium_Testsuits测试套件
  2. Django框架-Form组件
  3. 关于JavaScript的数组随机排序
  4. 数据结构与算法 Python语言描述 笔记
  5. [转载] 手工制作Win7 OEM版
  6. HihoCoder 1323 回文字符串
  7. mysql在空闲8小时之后会断开连接(默认情况)
  8. Dojo学习笔记(三):类化JavaScript
  9. Informix IDS 11零碎治理(918考试)认证指南,第 7 部分: IDS复制(24)
  10. MailMail升级到1.0.2.4