【面向对象】 JML(Level 0) 学习笔记
JML Level 0 学习笔记
- 注释结构
- JML表达式
- 原子表达式
- 量化表达式
- 集合表达式
- 操作符
- 方法规格
- 类型规格
- 约束限制
- 方法与类型规格的关系
- 一个完整例子
JML是用于对Java程 在这里插入代码片
序进行规格化设计的一种表示语言。通过JML及其支持工具,不仅可以基于规格自动测试用例,并整合了SMT Solver等工具以静态方式来检查代码实现对规格的满足情况。JML的两种用法: 开展规格化设计和 提升代码可维护性。
注释结构
JML以javadoc注释的方式表示规格,每行以@起头。注释方式有两种:行注释//@ annotation
和块注释/*@ annotation @*/
。注释一般放在被注释成分的临近上部。
package org.jmlspecs.samples.jmlrefman; // line 1// line 2
public abstract class IntHeap { // line 3// line 4//@ public model non_null int [] elements; // line 5// line 6/*@ public normal_behavior // line 7@ requires elements.length >= 1; // line 8@ assignable \nothing; // line 9@ ensures \result // line 10@ == (\max int j; // line 11@ 0 <= j && j < elements.length; // line 12@ elements[j]); // line 13@*/ // line 14public abstract /*@ pure @*/ int largest(); // line 15// line 16//@ ensures \result == elements.length; // line 17public abstract /*@ pure @*/ int size(); // line 18
}; // line 19
第1、3、15、8、19行为有效的代码。抽象类IntHeap中包含largest()和size()两个抽象方法。
第15、18行:/*@ pure @*/
表示为纯粹查询方法(不会有任何副作用)。
第5行:IntHeap所管理的数据规格。int[] elements是规格层次描述,不一定需要有一模一样的属性定义。non_null表示elements数组对象引用不能为null。如果是静态规格变量,声明为//@ public static model non_null int[] elements
;实例规格变量则为//@ public instance model non_null int[] elements
第17行:size方法后置条件(postcondition)。\result为JML关键字,表示返回结果。该句表示任何时候执行方法都返回elements.length。
第7-14行:该段内容表示largest的规格,包括三个部分:
(1) requires定义前置条件
(2) assignable副作用范围限定
\nothing关键字表示不对任何属性进行修改(pure方法)
(3) ensures定义后置条件
返回elements中最大的那个
JML表达式
注意:JML断言中不可以使用带有赋值语句的操作符
原子表达式
- \result : 非void型方法执行结果。
- \old(expr) : 表示表达式expr在相应方法执行前的取值。
该表达式涉及到评估 expr 中的对象是否发生变化,但是针对一个对象引用而言,只能判断引用本身是否发生变化,而不能判断引用所指向的对象实体内容是否发生变化。 - \not_assigned(x,y,…) : 用来表示括号中的变量是否在方法执行过程中被赋值。
该表达式主要用于后置条件的约束表示上(限制一个方法的实现不能对列表中的变量进行赋值)。 - \not_modifed(x,y,…) : 表达式限制括号中的变量在方法执行期间的取值未发生变化。
- \nonnullelements(container) : 表示 container 对象中存储的对象不会有 null。等价于:
container != null &&
(\forall int i; 0 <= i && i < container.length; \\ forall为针对所有i
container[i] != null)
- \type(type) : 返回类型type对应的类型(Class)。
e.g. type( boolean )为Boolean.TYPE
- \typeof(expr) : 该表达式返回expr对应的准确类型。
e.g. \typeof( false )
量化表达式
- \forall : 全称量词修饰的表达式,表示对于给定范围内的元素,每个元素都满足相应的约束。
(\forall inti,j; 0 <= i && i < j && j < 10; a[i] < a[j])
// 如果表达式为真,则说明 a是升序数组
- \exists : 存在量词修饰的表达式。
(\existsint i; 0 <= i && i < 10; a[i] < 0)
// 针对 0 <= i < 10,存在 a[i]小于0
- \sum : 返回给定范围内的表达式的和。
(\sum int i; 0 <= i && i < 5; i)
// 计算 0 + 1 + 2 + 3 + 4
- \product : 返回给定范围内的表达式的连乘结果。
(\product int i; 0 < i && i < 5; i)
// 计算 1 * 2 * 3 * 4
- \max : 返回给定范围内的表达式的最大值。
(\max int i; 0 <= i && i < 5; i)
- \min : 返回给定范围内的表达式的最小值。
(\min int i; 0 <= i && i < 5; i)
- \num_of : 返回指定变量中满足相应条件的取值个数。
(\num_of int x; 0<x && x<=20;x%2==0)(\num_of T x;R(x);P(x)) == (\sum T x;R(x)&&P(x);1) // 0 到 20中能被 2整除的数的个数,得 10
集合表达式
可以构造一个局部的集合(容器),明确集合中可以包含的元素。
new ST {T x|R(x)&&P(x)}
// 其中的 R(x)对应集合中 x的范围,通常是来自于某个既有集合中的元素,如 s.has(x)
// P(x)对应 x取值的约束
new JMLObjectSet {Integer i | s.contains(i) && 0 < i.intValue() }
// 表示构造一个 JMLObjectSet对象,其中包含的元素类型为 Integer
// 该集合中的所有元素都在容器集合s中出现(注:该容器集合指 Java程序中构建的容器,比如 ArrayList),且整数值大于0。
操作符
- 子类型关系操作符
E1<:E2
// 如果 E1是 E2的子类型或者与 E2类型相同,则返回真
// 注意任何类型都是 Object类型的子类型
- 等价关系操作符
b_expr1<==>b_expr2
b_expr1<=!=>b_expr2
// <==> 比 == 的优先级低
// <=!=> 比 != 的优先级低
- 推理操作符
b_expr1==>b_expr2
// 当 b_expr1==false || b_expr1==true || b_expr2==true 时,为 true
b_expr2<==b_expr1
- 变量引用操作符
可以引用Java代码;JML中的变量;\nothing(空集);\everything(全集,当前作用域下能访问到的所有变量)
assignable \nothing
// 当前作用域下每个变量都不可以在方法执行过程中被赋值
方法规格
方法规格的核心内容包括三个方面:前置条件、后置条件和副作用约定。
- 前置条件 : 对方法输入参数的限制,不满足则不能保证正确性。
requires P;
// 方法实现者确保方法执行返回结果一定满足谓词 P的要求,即确保 P为真
public /*@ pure @*/ String getName(); // pure表示不会改变对象
//@ ensures \result == bachelor || \result == master;
public /*@ pure @*/ int getStatus();
//@ ensures \result >= 0;
public /*@ pure @*/ int getCredits();
// 有些前置条件可以引用 pure方法返回的结果
/*@ requires c >= 0;
@ ensures getCredits() == \old(getCredits()) + c;
@*/
public void addCredits(int c);
- 后置条件 : 对方法执行结果的限制,执行后如果满足则执行正确。
ensures P;
// 在前置或者后置条件中一个容器中所有变量约束
/*@ requires size < limit && !contains(elem);
@ ensures \result == true;
@ ensures contains(elem);
@ ensures (\forall int e;
@ e != elem;
@ contains(e) <==> \old(contains(e)));
@ ensures size == \old(size) + 1;
@*/
public boolean add(int elem) {/*...*/}/*@ ensures !contains(elem);
@ ensures (\forall int e;
@ e != elem;
@ contains(e) <==> \old(contains(e)));
@ ensures \old(contains(elem)) ==> size == \old(size) - 1;
@ ensures !\old(contains(elem)) ==> size == \old(size);
@*/
public void remove(int elem) {/*...*/}
// 如果输入参数在容器中,则移除该整数;不能移除容器中不等于输入参数的任何整数
- 副作用约定 : 指方法在执行过程中对输入对象或 this 对象进行了修改(对
其成员变量进行了赋值,或者调用其修改方法)。
方法在执行过程中会修改对象的属性数据或者类的静态成员数据,从而给后续方法的执行带来影响。从方法规格的角度,必须要明确给出副作用范围。、
注1:不允许在副作用约束子句中指定规格声明的变量数据。
注2:private量通常调用者不可见。可以用/*@ spec_public @*/
注释一个类的私有成员变量,表示在规格中可以直接使用,从而调用者可见。
public class IntegerSet{private /*@spec_public@*/ ArrayList<Integer> elements;private /*@spec_public@*/ Integer max;private /*@spec_public@*/ Integer min;/*@@ ...@ assignable \nothing; // assignable(可赋值)@ assignable \everything;@ modifiable \nothing; // modifiable(可修改)@ modifiable \everthing;@ assignable elements;@ modifiable elements;@ assignable elements, max, min;@ modifiable elements, max, min;@*/
}
课程区分两类方法:全部过程和局部过程。前者对应着前置条件恒为真,即可以适应于任意调用场景;后者则提供了非恒真的前置条件,要求调用者必须确保调用时满足相应的前置条件。
从设计角度,软件需要适应用户的所有可能输入,因此也需要对不符合前置条件的输入情况进行处理,往往对应着异常处理。从规格的角度,JML区分这两种场景,分别对应正常行为规格(normal_behavior)和异常行为规格(expcetional_behavior)。
/*@ public normal_behavior
// public normal_behavior 表示接下来的部分对方法的正常功能给出规格
@ requires z <= 99;
@ assignable \nothing;
@ ensures \result > z;
@ also
// also表示还有异常功能存在或者子类重载父类需要补充规则
@ public exceptional_behavior
// exceptional_behavior 表示接下来的部分对方法的正常功能给出规格
@ requires z < 0;
@ assignable \nothing;
@ signals (IllegalArgumentException e) true;
@*/
public abstract int cantBeSatisfied(int z) throws IllegalArgumentException;
这个例子其实是错误的!
- signal
signals (***Exception e) b_expr
// 当 b_expr 为 true 时,方法会抛出括号中给出的相应异常e
signals_only ***Exception e
// 满足前置条件则抛出异常public abstract class Student {/** A specification that can't be satisfied. *///@ public model non_null int[] credits;/*@ normal_behavior@ requires z >=0 && z <= 100;@ assignable \nothing;@ ensures \result == credits.length;@ also@ exceptional_behavior@ requires z < 0;@ assignable \nothing;@ signals_only IllegalArgumentException;@ also@ exceptional_behavior@ requires z > 100;@ assignable \nothing;@ signals_only OverFlowException;@*/
public abstract int recordCredit(int z) throws IllegalArgumentException,
OverFlowException;
}
// 根据输入参数的取值范围抛出不同的异常
类型规格
约束限制
类型规格指针对Java程序中定义的数据类型所设计的限制规则,一般而言,就是指针对类或接口所设计的约束规则。
- 不变式限制(invariant)
不变式(invariant)是要求在所有可见状态下都必须满足的特性。
invariant P
可见状态(visible state):
1. 对象的有状态构造方法(用来初始化对象成员变量初值)的执行结束时刻
2. 在调用一个对象回收方法(finalize方法)来释放相关资源开始的时刻
3. 在调用对象o的非静态、有状态方法(non-helper)的开始和结束时刻
4. 在调用对象o对应的类或父类的静态、有状态方法的开始和结束时刻
5. 在未处于对象o的构造方法、回收方法、非静态方法被调用过程中的任意时刻
6. 在未处于对象o对应类或者父类的静态方法被调用过程中的任意时刻
凡是会修改成员变量(包括静态成员变量和非静态成员变量)的方法执行期间,对象的状态都不是可见状态(完整可见)。在方法执行期间,对象的不变式有可能不满足。因此,类型规格强调在任意可见状态下都要满足不变式。
public class Path{private /*@spec_public@*/ ArrayList <Integer> seq_nodes;private /*@spec_public@*/ Integer start_node;private /*@spec_public@*/ Integer end_node;/*@ invariant seq_nodes != null &&@ seq_nodes[0] == start_node &&@ seq_nodes[seq_nodes.legnth-1] == end_node &&@ seq_nodes.length >=2;@*/
}
静态不变式(static invariant) 和 实例不变式(instanceinvariant)。其中静态不变式只针对类中的静态成员变量取值进行约束,而实例不变式则可以针对静态成员变量和非静态成员变量的取值进行约束。可以在不变式定义中明确使用instance invariant
或 static invariant
来表示不变式的类别。
- 状态变化约束(constraint)
用constraint来对前序可见状态和当前可见状态的关系进行约束。
public class ServiceCounter{private /*@spec_public@*/ long counter;//@ invariant counter >= 0;//@ constraint counter == \old(counter)+1;
}
static constraint指涉及类的静态成员变量,而instance constraint则可以涉及类的静态成员变量和非静态成员变量。
方法与类型规格的关系
静态成员初始化 | 有状态静态方法 | 有状态构造方法 | 有状态非静态方法 | |
---|---|---|---|---|
static invariant | 建立 | 保持 | 保持 | 保持 |
instance invariant | (无关) | (无关) | 建立 | 保持,除非是finalizer方法 |
注:“建立”的含义是静态成员建立了满足相应不变式的类或对象状态。“保持”的含义是如果方法执行前不变式满足,执行后还应该满足相应的不变式。
静态成员初始化 | 有状态静态方法 | 有状态构造方法 | 有状态非静态方法 | |
---|---|---|---|---|
static constraint | (无关) | 遵从 | 遵从 | 遵从 |
instance constraint | (无关) | (无关) | (无关) | 遵从 |
注:“遵从”的含义是成员变量的当前取值和上一个取值之间的关系满足constraint的规定,即“遵从规定”。
一个完整例子
public class Student {// 几个变量是私有的private /*@ spec_public @*/ String name;//@ public invariant credits >= 0;private /*@ spec_public @*/ int credits;/*@ public invariant credits < 180 ==> !master &&@ credits >= 180 ==> master;@*/private /*@ spec_public @*/ boolean master;/*@ requires sname != null;@ assignable \everything;@ ensures name == sname && credits == 0 && master == false;@*/public Student (String sname) {name = sname;credits = 0;master = false;}/*@ requires c >= 0;@ ensures credits == \old(credits) + c;@ assignable credits, master;@ ensures (credits > 180) ==> master@*/public void addCredits(int c) {updateCredits(c);if (credits >= 180) {changeToMaster();}} /*@ requires c >= 0;@ ensures credits == \old(credits) + c;@ assignable credits;@*/private void updateCredits(int c) {credits += c;} /*@ requires credits >= 180;@ ensures master;@ assignable master;@*/private void changeToMaster() {master = true;} /*@ ensures this.name == name;@ assignable this.name;@*/public void setName(String name) {this.name = name;} /*@ ensures \result == name;@*/public /*@ pure @*/ String getName() {return name;}
}
【面向对象】 JML(Level 0) 学习笔记相关推荐
- flink1.12.0学习笔记第1篇-部署与入门
flink1.12.0学习笔记第 1 篇-部署与入门 flink1.12.0学习笔记第1篇-部署与入门 flink1.12.0学习笔记第2篇-流批一体API flink1.12.0学习笔记第3篇-高级 ...
- Quartz.NET 2.0 学习笔记(5) :实例创建Windows服务实现任务调度 Quartz.NET 项目地址 http://quartznet.sourceforge.net/ Quar
Quartz.NET 2.0 学习笔记(5) :实例创建Windows服务实现任务调度 Quartz.NET 项目地址 http://quartznet.sourceforge.net/ Quartz ...
- mysql5.0镜像_Mysql5.0学习笔记(一)
Mysql5.0学习笔记(一) -基本sql语句与支持字符集 1.登录 mysql -h localhost -u root 2.创建用户firstdb(密码firstdb)和数据库,并赋予权限于fi ...
- Zabbx6.0(学习笔记)
Zabbx6.0(学习笔记) 目录导航 Zabbx6.0(学习笔记) 一.为什么 需要监控系统 二.如何选择监控 三.Zabbix概述 四.Zabbix安装哪个版本? Zabbix安装要求 1.硬件 ...
- flink1.12.0学习笔记第2篇-流批一体API
flink1.12.0学习笔记第 2 篇-流批一体API flink1.12.0学习笔记第1篇-部署与入门 flink1.12.0学习笔记第2篇-流批一体API flink1.12.0学习笔记第3篇- ...
- CCC3.0学习笔记_认证和隐私保护
CCC3.0学习笔记_Authentication and Privacy Keys 系列文章目录 文章目录 系列文章目录 前言 1. 手机端和车厂服务器端的密钥存储 2. 密钥的产生和使用的说明 3 ...
- TensorFlow2.0 学习笔记(三):卷积神经网络(CNN)
欢迎关注WX公众号:[程序员管小亮] 专栏--TensorFlow学习笔记 文章目录 欢迎关注WX公众号:[程序员管小亮] 专栏--TensorFlow学习笔记 一.神经网络的基本单位:神经元 二.卷 ...
- 《TP5.0学习笔记---配置篇》
TP5.0学习笔记 TP5目录结构介绍 application目录是应用目录,我们整个应用所有的内容都写在这个目录中,在后续开发中,我们更多的时候都是在编写这个目录中的文件.在它里边有一个index文 ...
- Tensorflow2.0学习笔记(一)
Tensorflow2.0学习笔记(一)--MNIST入门 文章目录 Tensorflow2.0学习笔记(一)--MNIST入门 前言 一.MNIST是什么? 二.实现步骤及代码 1.引入库 2.下载 ...
最新文章
- Spring Security实战教程2021版即将下线,2022版即将上线!
- CSS完美兼容IE6/IE7/FF的通用方法
- vue-cli打包遇到的问题
- 向量收敛在matlab中,matlab实验报告
- 360加固分析(一)
- 使命召唤手游迎来欧阳娜娜,这阵容够豪华,玩家期待吗?
- mac php mcrypt,MacOSX 10.10安装mcrypt详细教程分享
- 骆驼路线的主/从故障转移
- centos 删除crontab_centos下crontab的使用
- ajax请求头cookies中传递sid,跨域请求单点登录,登录成功,但是在controller中获取的cookie与浏览器中不一致,请大神指点一二。^_^...
- 《中国机长》和《我和我的祖国》合计票房破31亿 两片贡献国庆档八成票房
- mysql日志存储类型_msyql 日志分类、存储、慢查询日志
- binwalk 提取bootimg_boot.img的解包与打包
- python实现找到给定列表中满足给定和的所有子列表,元素可重复使用
- LockSupport类中的park()和unpark()
- SAS入门教程1---SAS系统简介
- VirtualBox安装Mac OS 10.11——虚拟机安装黑苹果
- 陶渊明《移居》二首的赏析
- 360众测考核简单记录
- python 网格交易源码_文华财经网格交易源码