软件构造学习笔记(四)
第四讲 数据类型和类型检验
1 编程中的数据类型
Java中的数据类型可分为基本类型和对象类型,具体如下表所示:

所有的对象类型都由最初的类Object继承而来,形成层次结构。
基本类型有相应的对象类型(包装),一般可以自动转换。
2 静态/动态类型语言
Java是一种静态类型的语音,Java中所有的变量类型在编译时已知,因此编译器可以推导出所有表达式的类型
而类似python这类直到运行时才知道变量类型的语音被称为动态类型语言
2 静态/动态数据类型检测
一种语言所能提供的三种自动检查:
- 静态检查:在程序运行之前就能自动发现错误,因此避免了将错误带入到运行阶段,可提高程序正确性/健壮性。
可以发现语法错误、类名/函数名错误、函数的参数数目和类型错误、函数实现体中的返回值类型错误,是关于“类型”的检查。
- 动态检查:当代码被执行时会自动发现错误,可以发现非法参数值(如x/y, y=0)、非法返回值、数组越界、空指针引用等错误,是关于“值”的检查。
- 不检查。
毋庸置疑,静态检查比动态检查好,而动态检查又比不检查好。
3 不变性和可变性
不变性是一个重要的设计原则。
不变数据类型:一旦被创建,其值不能改变。
不变对象:一旦被创建,始终指向同一个值/引用。
可变对象:拥有方法可以修改自己的值/引用。
基本类型及其封装对象类型都是不可变的。
为了使参数不变,可以用关键字final修饰。
final可以限制一个不可变的/不可重新分配的对可变值的引用(ps:final StringBuilder sb)或可变的引用也可指向比可变的值。
在Java中,用final修饰的变量在首次赋值后发生改变,编译器在静态类型检查时会提示错误。
编程时应尽量使用final变量作为方法的输入参数、作为局部变量。
同时关键字final也代表了程序员的一种“设计决策”。
final类无法派生子类、final变量无法改变值/引用、final方法也无法被子类重写。
可以使用Collections.unmodifiableList()方法将可变类型包装为不可变类型。
String为不变类型,其相应的可变类型为StringBuilder.
使用可变类型的优势:
可变类型因为最少化拷贝,可以提高效率;
可变数据类型适合于在多个模块之间共享数据,可获得更好的性能。
使用不可变类型的优势:
不可变类型更“安全”,在其他质量指标上表现更好。
4 Snapshot图解
Snapshot图用于描述程序运行时的内部状态。
双线箭头指的是不可变的引用,双线椭圆指的是不可变的对象。


5 复杂数据类型
Array: 定长数组。
List: 顺序表,是一个抽象接口,有ArrayList等实现。
Set: 集合。
Map: 类似于字典的抽象接口,有HashMap等实现。
Iterator,迭代器,一个对象,遍历一组元素并逐个返回元素。
迭代器有两种方法,next()返回下一个元素,是可变方法;hasNext()是测试是否到达一组数据的结束的。
当添加一个item时,编译器执行静态检查,确保只添加合适类型的item。因此,可确保取出的值是指定类型的。
基本类型及其封装对象类型都是不可变的。Java的包装器得到的结果是不可变的,但是这种“不可变”是在运行阶段获得的,编译阶段无法据此进行静态检查。
不可修改的包装主要有两个用途:使集合在构建后保持不变;允许某些客户端以只读方式访问您的数据结构。
第五讲 设计规约
规约可以看做是用户和实现者间的防火墙。

1 规约的意义
规约可以隔离“变化”,无需通知客户端,也可以提高编码效率。
精确的规约,有助于区分责任。客户端无需阅读被调用函数的代码,只需理解规约即可。
根据规约可判断具体的实现是否行为等价。
2 规约的结构
规约只讲“能做什么”,不讲“怎么实现”。
前置条件:对客户端的约束,在使用方法时必须满足的条件。
后置条件:对开发者的约束,方法结束时必须满足的条件。
契约:如果前置条件满足了,后置条件必须满足。
静态类型声明是一种规约,可据此进行静态类型检查static checking.
方法前的注释也是一种规约,但需人工判定其是否满足。

根据规约可设计黑盒测试用例。
3 设计规约
规约确定了一个范围,如果某个具体的实现满足规约,则在其范围内;否则就在范围外。

程序员可以在规约范围内自由实现函数,而客户端无需了解具体如何实现。
规约越强,则确定的范围就越小,实现的自由度更低。
更强的后置条件意味着实现的自由度更低了➔在图中的面积更小
更弱的前置条件意味着实现时要处理更多的可能输入,实现的自由度低了➔面积更小

当然也会出现规约强度无法比较的情况,二者的规约范围可能有一定交集。
强的规约:更放松的前置条件+更严格的后置条件。
好的规约应该是内聚的(功能单一、简单、易理解)、信息丰富的、足够强的(有利客户端)、适当弱的(便于实现)。
在设计时应尽量使用抽象数据类型,提供更大的自由度。
私有方法可以使用前置条件,公有方法可以放松或不适用前置条件。
浙公网安备 33010602011771号