第三章 ADT OOP
基本数据类型和对象数据类型
对象数据类型:
String BigInteger 还有什么class Array等等
静态类型检查和 动态类型检查
在编译中进行检查:非法的参数值. 非法的返回值 越界, 空指针
静态检查:关于“类型”的检查,不考虑值 动态检查:关于“值”的检查
Mutability and Immutability
immutablility: int,string
一旦被创建,始终指向同一个值/引用 , 使用不可变类型,对其频繁修改会产生大量的临时拷贝(需要垃圾回收),不可变类型更“安全”, 在其他质量指标上表现更好。
mutablility: LIst,set,StringBuilder
拥有方法可以修改自己的值/引用
final可以将一个可变数据类型修改为不变数据类型,(不可变的引用:)
Snapshot diagrams
不可变对象:用双线 椭圆
引用是不可变的,但指向 的值却可以是可变的 可变的引用,也可指向不可 变的值
不可变的引用用=>表示,final修饰了
经过这个包装后的map,set,list等变得不能被调用内部属性方法对本身进行值的修改了!!
Specification
Behavioral equivalence (行为等价性):根据规约判断是否行为等价
precondition:对客户端的约束,在使用方法时必须满足的条,@parm
postcondition:对开发者的约束,方法结束时必须满足的条件 @return @throws.
契约:如果前置条件满足了,后置条件必须满足
– 前置条件不满足,则方法可做任何事情。 – “你违约在先,我自然不遵守承诺”
静态类型声明是一种规约,可据此进行 静态类型检查static checking
除 非在后置条件里声明过,否则方法内部不应该改变输入参数 尽量避免使用mutable的对象
. spec变强:更放松的前置条件+更严格的后置条件。
越强的规约,意味着implementor的自由度和责任越重,而client的 责任越轻。
确定的规约:给定一个满足precondition的输入,其输出是唯一的、明确的
欠定的规约:同一个输入可以有多个输出
非确定的规约:同一个输入, 多次执行时得到的输出可能不同
声明式规约:没有内部实现的描述,只有 “初-终”状态
操作式规约,例 如:伪代码
Diagramming specifications
某个具体实现, 若满足规约,则落在其范围内;否则,在其之外。更强的规约,表达为更小的区域。
Abstract Data Type
Creator screate new objects of the type. 构造器.
Producers create new objects from old objects of the type. 生产器
Observers take objects of the abstract type and return objects of a different type. 观察器
Mutators change objects. 变值器,改变对象属性的方法
表示独立性
表示独立性:client使用ADT时无需考虑其内部如何实 现,ADT内部表示的变化不应影响外部spec和客户端。
测试creators, producers, and mutators:调用observers来观察这些 operations的结果是否满足spec; 测试observers:调用creators, producers, and mutators等方法产生或 改变对象,来看结果是否正确。
Invariants(表示不变量)
不变量:在任何时候总是true,为什么需要不变量:保持程序的“正确 性”,容易发现错误。Rep Invariant:
Abstraction Function
抽象函数:R和A之间映射关系的函数,即到用户关注抽象空间A的映射
选择某种特定的表示方式R,进而指定某个子集是“合 法”的(RI),并为该子集中的每个值做出“解释”(AF)——即如何映射 到抽象空间中的值。
即使是同样的R、同样的RI,也 可能有不同的AF,即“解释不同”。
1) 选择R和A;(2) RI --- 合法的表示值; (3) 如何解释合法的表示值 ---映射AF
checkRep() 随时检查RI是否满足
Safety from Rep Exposure
对于如果rep中有一个set a 的话,进行防御式拷贝的时候定义一个新的set然后set.addAll(a),就可以返回新的set达到了防止表示泄露。对于数组的话,进行防御使拷贝时候要用for循环来完成。
表示泄漏的风险:一旦泄露,ADT内部表示可能会在程序的任何位置 发生改变(而不是限制在ADT内部),从而无法确保ADT的不变量是 否能够始终保持为true
用不变量取代方法的Precondition
OOP:
静态方法是类方法,调用时不需要创建类实例。
继承与 Overriding
严格继承:子类只能添加新方法,无法重写超类中 的方法,这里也就是说用了final修饰类中的方法。
Overwriting a Method:
对某些子类型来说,有特殊性,故重 写父类型中的函数,实现自己的特殊要求。如果父类型中的某个函数实现体为空, 意味着其所有子类型都需要这个功能, 但各有差异,没有共性,在每个子类中 均需要重写。重写之后,利用super()复用了父类型 中函数的功能,并对其进行了扩展重写之后,利用super()复用了父类型 中函数的功能,并对其进行了扩展。
抽象类
如果某些操作是所有子类型都共有, 但彼此有差别,可以在父类型中设计抽象方法,在各子类型中重写。
Overloading
(1)方法重载是让类以统一的方式处理不同类型数据的一种手段。多个同名函数同时存在,具有不同的参数个数/类型。重载Overloading是一个类中多态性的一种表现。
(2)Java的方法重载,就是在类中可以创建多个方法,它们具有相同的名字,但具有不同的参数和不同的定义。调用方法时通过传递给它们的不同参数个数和参数类型来决定具体使用哪个方法, 这就是多态性。
(3)重载的时候,方法名要一样,但是参数类型和个数不一样,返回值类型可以相同也可以不相同。无法以返回型别作为重载函数的区分标准。
可以在同 一个类内重载,也可在子类中重载
泛型
泛型接口interface
泛型list set等:
1. 无边界的通配符(Unbounded Wildcards), 就是<?>, 比如List<?>.
无边界的通配符的主要作用就是让泛型能够接受未知类型的数据.
2. 固定上边界的通配符(Upper Bounded Wildcards):
使用固定上边界的通配符的泛型, 就能够接受指定类及其子类类型的数据. 要声明使用该类通配符, 采用<? extends E>的形式, 这里的E就是该泛型的上边界. 注意: 这里虽然用的是extends关键字, 却不仅限于继承了父类E的子类, 也可以代指显现了接口E的类.
使用固定下边界的通配符的泛型, 就能够接受指定类及其父类类型的数据. 要声明使用该类通配符, 采用<? super E>的形式, 这里的E就是该泛型的下边界. 注意: 你可以为一个泛型指定上边界或下边界, 但是不能同时指定上下边界.
Hashcode override
等价的对象必须有相同的hashCodeequals override
等价关系:自反、传递、对称行为等价性:调用对象的任何方法,允许调用两个对象的任何方法,包括mutator,都展示出一致的结果
对于不可变对象,观察相等和行为相等是完全等价的,因为它们没有改造者改变对象内部的状态。
对于可变对象,Java通常实现的是观察相等。例如两个不同的 List
对象包含相同的序列元素,那么equals()
操作就会返回真。
如果某个mutable的对象包含在集合 类中,当其发生改变后,集合类的行为不确定 务必小心