基本数据类型、对象数据类型
静态类型检查、动态类型检查
Java是静态类型语言:在编译阶段进行类型检查,可在编译阶段发现错误,避免了将错误带入到运行阶段,可提高程序正确性/健壮性
可检查出的错误:
Syntax errors语法错误
Wrong names 类名/函数名错误
Wrong number of arguments 参数数目错误
Wrong argument types 参数类型错误
Wrong return types 返回值类型错误
动态类型检查:在运行阶段进行类型检查
动态检查的阶段在静态检查之后,能检查到静态检查检测不到的错误,而对于某种类型的错误,静态检查更有优势
Illegal argument values 非法的参数值
Unrepresentable return values 非法的返回值
Out-of-range indexes 越界
Calling a method on a null object reference. 空指针
Mutable/Immutable
改变一个变量:将该变量指向另一个值的存储空间
改变一个变量的值:将该变量当前指向的值的存储空间中写入一个新的值。
Immutability 不变性:重要设计原则
不变数据类型:一旦被创建,其值不能改变,例如:String、 基本类型及其封装对象类型都是不可变的
如果是引用类型,也可以是不变的:一旦确定其指向的对象,不能再被改变
Final:类无法派生子类、变量无法改变值/引用、无法被子类重写
如果编译器无法确定final变量不会改变,就提示错误,这也是静态类型检查的一部分。
可变对象:拥有方法可以修改自己的值/引用,例如StringBuilder、Iterator
值的改变、引用的改变
使用不可变类型,对其频繁修改会产生大量的临时拷贝(需要垃圾回收)
可变类型最少化拷贝以提高效率,使用可变数据类型,可获得更好的性能,也适合于在多个模块之间共享数据
Mutability常见错误:改变一个传入参数的值、返回一个mutability类型
解决方法:通过防御式拷贝,给客户端返回一个全新的Date对象,大部分时候该拷贝不会被客户端修改,可能造成大量的内存浪费;如果使用不可变类型,则节省了频繁复制的代价
安全的使用可变类型:局部变量,不会涉及共享,只有一个引用;如果有多个引用(别名),使用可变类型就非常不安全
防御式拷贝例如:
Snapshot diagram
用于描述程序运行时的内部状态、便于程序员之间的交流、便于刻画各类变量随时间变化、便于解释设计思路
基本类型的值:
对象类型的值:
不可变对象:用双线椭圆
可变类型:
不可变的引用:用双线箭头
Specification、前置/后置条件
规约:程序与客户端之间达成的一致,Spec给“供需双方”都确定了责任,在调用的时候双方都要遵守,精确的规约,有助于区分责任,客户端无需阅读调用函数的代码,只需理解spec即可,规约可以隔离“变化”,无需通知客户端,规约也可以提高代码效率,规约:扮演“防火墙”角色
静态类型声明是一种规约,可据此进行静态类型检查static checking。
方法前的注释也是一种规约,但需人工判定其是否满足
除非在后置条件里声明过,否则方法内部不应该改变输入参数
应尽量遵循此规则,尽量不设计
mutating的spec,否则就容易引发bugs。
前置条件:对客户端的约束,在使用方法时必须满足的条件
后置条件:对开发者的约束,方法结束时必须满足的条件
契约:如果前置条件满足了,后置条件必须满足
前置条件不满足,则方法可做任何事情。
设计规约:
内聚的: Spec描述的功能应单一、简单、易理解
强弱适中:惯用做法是:
不限定太强的precondition,而是在postcondition中抛出异常:输入不合法;尽可能在错误的根源处fail,避免其大规模扩散
在规约里使用抽象类型,可以给方法的实现体与客户端更大的自由度
是否使用前置条件取决于:
(1) check的代价;(2) 方法的使用范围
如果只在类的内部使用该方法(private),那么可以不使用前置条件,在使用该方法的各个位置进行check——责任交给内部client;
如果在其他地方使用该方法(public),那么必须要使用前置条件,若client端不满足则方法抛出异常。
行为等价性
根据规约判断是否行为等价,两个函数符合相同规约,于是它们符合行为等价性。
规约的强度
spec变强:更放松的前置条件+更严格的后置条件
前置条件更弱,后置条件更强,就可以用S2替代S1
越强的规约,意味着implementor的自由度和责任越重,而client的责任越轻。
声明:本站部分文章及图片源自用户投稿,如本站任何资料有侵权请您尽早请联系jinwei@zod.com.cn进行处理,非常感谢!