数据类型
Primitive types 基本数据类型(8种) | Object types 对象数据类型(reference types 引用数据类型) |
---|---|
Short、int 、long、float、double、boolean、char、byte | 如:String、BigInteger |
只有值,没有ID(无法与其他值区分),不能赋值为null;immutable | 有值,也有ID;部分mutable,部分immutable |
在栈中分配内存,代价低 | 在堆中分配内存,代价高 |
静态类型检查&动态类型检查
静态类型检查 | 动态类型检查 |
---|---|
(静态类型语言 如java) 提高程序的正确性和健壮性 | (动态类型语言 如python) |
关于"类型"的检查,不考虑值(不知道运行时会是什么值) | 关于"值"的检查 |
Mutable & Immutable
Immutable 不可变数据类型 | mutable 可变数据类型 | |
---|---|---|
优点 | 安全 | 最少化拷贝以提高效率获得更好的性能,适合于在多个模块之间共享数据 |
缺点 | 频繁修改产生大量临时拷贝,需要垃圾回收· | 缺点:不安全 |
特点 | 一旦被创建,其值不能改变对于引用类型,加final限制不能改变引用 | 安全地使用可变类型:局部变量(不涉及共享,且只有一个引用)如果有多个引用(别名),不安全 Defensively copy 防御式拷贝:返回全新的对象 |
关于Java中的Mutable 和 Immutable类型
Snapshot Diagram
基本类型:单独一个常量
引用类型:圈住!
重分配:
不可变类型(用双线椭圆),修改引用
可变类型:修改值
引用:
可变引用:单线箭头
不可变引用:双线箭头
Specification
1.作用
2.内容:只讲"能做什么",而不讲"怎么实现"
3.Behavior equivalence 行为等价性
是否可以相互替换
4.规约的结构:
5.规约的强度与替换
6.deterministic spec & undetermined spec 确定的规约和欠定的规约
7.Declarative spec & operational spec 声明式规约和操作式规约
操作式规约:如 伪代码
声明式规约:没有内部实现的描述,只有"初-终"状态
声明式规约更有价值!
内部实现的细节不在规约里呈现,而放在代码实现体内部注释里呈现。
8.Diagraming specification
规约定义一个区域,该区域包含所有可能的实现方式。
空间中的每个点表示一种方法的实现。
对于某个具体实现,若满足规约,则落在其区域内。更强的规约表达为更小的区域。
9.Quality of specification 规约质量
内聚性:spec描述的功能应单一、简单、易理解
运行结果信息丰富(可能的改变,以及返回值等),不能让客户端产生理解上的歧义
足够强(如postcondition中充分阐述各种情况)
适当弱(太强的规约,在很多特殊情况下难以达到)
在规约里使用抽象类型(在java中,经常使用interface,如Map、List,而不是HashMap、ArrayList),可以给方法的实现体和客户端更大的自由度
使用前置条件和后置条件?
客户端不喜欢太强的pre-condition,不满足precondition的输入会导致失败
So:不限定太强的precondition,而在postcondition中抛出异常:输入不合法,
fail fast,避免fail大规模扩散
是否使用前置条件取决于:
check(检查参数合法性)的代价
方法的使用范围:
Pre-condition and post-condition 前置条件和后置条件
Pre-condition 前置条件(requires) | Post-condition 后置条件(effects) |
---|---|
@param | @return @throws |
对客户端的约束。在使用方法时必须满足的条件 | 对开发者的约束。方法结束时必须满足的条件 |
契约:如果前置条件满足了,后置条件必须满足。
除非在后置条件中声明,否则方法内部不应该改变输入参数。
尽量不设计mutating的spec,否则容易引发bugs。
尽量避免使用mutable对象。
避免使用可变的全局变量。
ADT
1.ADT及其四种操作
抽象类型:强调"作用于数据上的操作",程序员和client无需关心数据如何具体存储,只需设计/使用操作即可。
ADT由操作定义,与其内部实现无关。
可变数据类型:提供了可改变其内部数据值的操作;
不可变数据类型:其操作不改变内部值,而构造新的对象。(没有mutators)
ADT操作分类:
Representation Independence 表示独立性
Representation exposure 表示泄漏
Invariants 不变量 & Representation Invariant 表示不变量
Abstraction Function 抽象函数
表示空间R | 抽象空间A |
---|---|
值的实际实现本质 | 抽象表示(client看到和使用的值) |
ADT实现者关注表示空间R | 用户关注抽象空间A |
R到A的映射
一定是满射:A中元素总有R中具体的实现
未必是单射:A中一个元素在R中可能有多重实现方式
未必是双射:R中表示不符合A中需求(如图中"abbc")
抽象函数AF:R和A之间映射关系的函数
AF:R->A
对于RI :R-> Boolean
RI:某个具体的"表示"是否合法;表示值的一个子集,包含所有合法的表示值;一个条件,描述了什么是"合法"表示值。
Documenting AF 、RI、Safety from Rep Exposure
选择某种特定的表示方式R
进而指定某个子集是"合法"的(RI)
并为该子集中的每个值做出"解释"(AF)
即如何映射
Safety from Rep Exposure
证明代码并未对外泄露其内部表示
保证不变量为true,不变量:
关于ADT的学习总结
OOP
Interface 接口
Inheritance、override 继承和重写
Polymorphism ,subtyping and overloading 多态,子类型化,重载
三种多态:
Ad hoc polymorphism (特殊多态)
用于function overloading(功能重载),即重载
Parametric polymorphism (参数化多态)
泛型
更多关于泛型的内容
Subtyping (subtype polymorphism / inclusion polymorphism )(子类型多态、包含多态)
1.Overloading 重载
重载条件:
重载是一种静态多态,静态类型检查
(static dispatch 静态分派)并在编译阶段决定具体执行哪个方法(即对方法的调用取决于编译时声明的引用的类型)
重写(dynamic dispatch 动态分派)则进行动态类型检查,根据运行时堆中的实例类型选择方法。
2.Generic 泛型
通配符 > :只有使用泛型的时候出现,不能在定义中出现。
类型擦除:编译后、运行时类型擦除
List
注意可能引起重载编译错误。
运行时不能用 instanceof 检查泛型。
不能创建泛型数组
不能用在静态变量
不能创建对象(不能new)
3.Subtypes
超类的子类型,如:ArrayList和LinkedList是List的子类型。
4.Dispatch 分派
Static dispatch 静态分派 | Dynamic dispatch 动态分派 |
---|---|
将调用的名字与实际方法的名字联系起来(可能有多个) | 决定具体执行哪一个操作 |
重载,在编译阶段即可确定执行哪个具体操作 | 重写,在运行时决定 |
Early/static binding | Lade/dynamic binding |
绑定static、private、final方法时发生 | 重写父类子类的同样方法 |
![]() |
![]() |
equals()
1.引用等价性 ==
2.对象等价性 equals()
hashCode()
Equality of Mutable Types 可变对象的等价性
Observational equality 观察等价性 | Behavioral equality 行为等价性 |
---|---|
在不改变状态的形况下,两个mutable看起来是否一致 | 调用对象的任何方法都展示出一致的结果 |
调用observer,producer,creator | 调用任何方法,包括mutator |
当前情况下,看起来(如成员变量)相同 | 经过改变后,依然相同(只是别名引用) |