当前位置:网站首页>【HIT-SC-MEMO3】哈工大2022软件构造 复习笔记3
【HIT-SC-MEMO3】哈工大2022软件构造 复习笔记3
2022-08-04 05:32:00 【XMeow】
三、ADT & OOP
3.1 数据类型和类型检查
基本数据类型 & 对象数据类型
Primitives | Object Reference Types |
---|---|
int, long, byte, short, char, float, double, boolean | Classes, interfaces, arrays, enums,annotations |
只有值,没有ID (与其他值无法区分) | 既有ID,也有值 |
不可变 | 可变/不可变 |
在栈中分配内存 | 在堆中分配内存 |
Can’t achieve unity of expression | Unity of expression with generics |
代价低 | 代价昂贵 |
静态类型检查 & 动态类型检查
静态类型检查
关于“类型的检查”,不考虑值
在编译阶段发现错误,避免将错误带入运行阶段
提高程序的正确性、健壮性
- 静态类型检查错误:
- 语法错误
- 类名/函数名错误
- 参数数目错误
- 参数类型错误
- 返回值类型错误
- 动态类型检查
- 关于“值”的检查
- 动态类型检查错误:
- 非法的参数值
- 非法的返回值
- 越界
- 空指针
Mutable可变 & Immutable不可变
不变对象:一旦被创建,始终指向同一个值
可变对象:拥有方法可以修改自己的值/引用
final
尽量使用final
作为方法的输入参数、作为局部变量final
表明了程序员的一种“设计决策” final
类无法派生子类 final
变量无法改变值/引用final
方法无法被子类重写
String 不可变
StringBuilder 可变
/* String部分 */
String s = "a"; //开辟一个存储空间,里面存着字符a,s指向这块空间,记为space1
String t = s; //让t指向s所指向的空间即space1
s = s.concat("b"); //把字符a和字符b连接,然后把“ab”放在一个新的存储空间,记为space2,最后让s指向这块空间
//我们可以看到,现在s和t所指向的是两块不同的空间,空间中的内容也不一样,因此s和t的效果是不一样的
/* StringBuilder部分 */
StringBuilder sb = new StringBuilder("a"); //开辟一个存储空间,里面存着字符a
StringBuilder tb = sb; //开辟一个存储空间,里面存着字符a
sb.append("b"); //取出a,然后与字符b连接,然后把“ab”仍然放在这块空间内,把原来的“a”覆盖了,sb的指向没变
//在这个情况下,由于从始至终只用到了一块存储空间,所以sb和tb的效果实际上是相同的
mutable 优点:
拷贝:不可变类型,频繁修改会产生大量的临时拷贝,需要垃圾回收;可变类型,最少化拷贝,以提高效率
获得更好的性能
模块之间共享数据
UnmodifiableCollections:Java设计有不可变的集合类提供使用
值的改变 & 引用的改变
- “改变一个变量”:将该变量指向另一个值的存储空间(引用)
- “改变一个变量的值”:将该变量当前指向的值的存储空间中写入一个新的值
表示泄露和防御式拷贝
通过防御式拷贝,给客户端返回一个全新的对象(副本),客户端即使对数据做了更改,也不会影响到自己。例如:
return new Date(groundhogAnswer.getTime());
大部分时候该拷贝不会被客户端修改,可能造成大量的内存浪费
如果使用不可变类型,则节省了频繁复制的代价
Snapshot diagram
运行时、代码层面、瞬时
- 基本类型的值
- 对象类型的值
- 可变对象:单线圈
- 不可变对象:双线圈
- 不可变的引用:双线箭头
- 可变的引用:单线箭头
- 引用是不可变的,但指向的值却可以是可变的
- 可变的引用,也可指向不可变的值
例:用Snapshot表示String和StringBuilder的区别
集合类Snapshot图
- List
- Set
- Map
3.2 设计规约(Specification)
Spec概念
程序和客户端达成的一致
作用:
- 给“供需双方”都确定了责任并区分责任,调用时双方都要遵守(客户端只需要理解Spec即可)
- 隔离“变化”、降低耦合度
- 不需要了解具体实现
要素:
- 输入数据类型(客户端约束)
- 输出数据类型(内部实现约束)
前置条件 & 后置条件
- 前置条件:For 客户端
- 后置条件:For 开发者
- 契约:
前置条件满足了,后置条件必须满足;
前置条件不满足,后置条件不一定满足(输入错误,可以抛出异常)。
行为等价性
- 站在客户端角度、根据规约:功能是否等价
例:以下两段代码是否等价
规约:
解:
- 当
val
在范围内时,两者返回相同; - 当
val
不在范围内时,前者返回arr.length
,后者返回-1
; - 根据规约,两者效果相同,因此等价。
Spec的写法
方法注释
@param
@return
@throws
输入类型、返回类型
一个好的Spec应该:
- 内聚的
Spec描述的功能应单一、简单、易理解
规约做了两件事,所以要分离开形成两个方法。- 信息丰富的
不能让客户端产生理解歧义- 足够“强”
太弱的spec,客户不放心
开发者应尽可能考虑特殊情况,在post-condition给出处理措施- 足够“弱”
太强的spec,在很多特殊情况下难以达到,给开发者增加了实现的难度(client当然非常高兴)
Spec的强度
- 前置条件越弱,规约强度越强;
- 后置条件越强,规约强度越强;
- 规约越强,开发者责任越重,客户端责任越轻;
- 某个具体实现,若满足规约,则落在其范围内;否则,在其之外。
- 程序员可以在规约的范围内自由选择实现方式;
- 更强的规约,表达为更小的区域;
3.3 抽象数据类型(ADT)
设计ADT:规格Spec–>表示Rep–>实现Impl
四类ADT操作
- Creators
实现:构造函数constructor或静态方法(也称factory method) - Producers
需要有“旧对象”return
新对象
eg.String.concat()
- Observers
eg.List
的.size()
- Mutators
改变对象属性
若返回值为void
,则必然改变了对象内部状态(必然是mutator)
表示独立性
- client使用ADT时无需考虑其内部如何实现,ADT内部表示的变化不应影响外部spec和客户端。
抽象函数AF & 表示不变量RI
抽象值构成的空间(抽象空间):客户端看到和使用的值
程序内部用来表示抽象值的空间(表示空间):程序内部的值
Mapping:满射、未必单射(未必双射)
ADT开发者关注表示空间R,client关注抽象空间A
抽象函数(AF):
- R和A之间映射关系的函数
- 即如何去解释R中的每一个值为A中的每一个值。
- AF : R → A
- R中的部分值并非合法的,在A中无映射值
表示不变性(RI):
- 某个具体的“表示”是否是“合法的”
- 所有表示值的一个子集,包含了所有合法的表示值
- 一个条件,描述了什么是“合法”的表示值
检查RI:
随时检查RI是否满足
在所有可能改变rep的方法内都要检查
Observer方法可以不用,但建议也要检查,以防止你的“万一”
测试ADT
因为测试相当于client使用ADT,所以它也不能直接访问ADT内部的数据域,所以只能调用其他方法去测试被测试的方法。
针对creator:构造对象之后,用observer去观察是否正确
针对observer:用其他三类方法构造对象,然后调用被测observer,判断观察结果是否正确
针对producer:produce新对象之后,用observer判断结果是否正确
以注释的形式撰写AF、RI、 Safety from Rep Exposure
- 在代码中用注释形式记录AF和RI
- 精确的记录RI:rep中的所有fields何为有效
- 精确记录AF:如何解释每一个R值
- 表示泄漏的安全声明
- 给出理由,证明代码并未对外泄露其内部表示——自证清白
3.4 面向对象编程(OOP)
接口(Interface)& 抽象类(Abstract Class)& 具体类(Concrete Class)
接口:定义ADT
类:实现ADT
Concrete class --> Abstract Class --> Interface
接口:
- 接口之间可以继承与扩展
- 一个类可以实现多个接口(从而具备了多个接口中的方法)
- 一个接口可以有多种实现类
抽象类:
- 至少有一个抽象方法
- 抽象方法 Abstract Method
- 未被实现
- 如果某些操作是所有子类型都共有,但彼此有差别,可以在父类型中设计抽象方法,在各子类型中重写
具体类:
- 实现所有父类未实现的方法
继承(Inheritance) & 重写(Override)
类 & 类:继承
类 & 接口:实现、扩展
覆盖/重写Override
:
- 重写的函数:完全同样的signature
- 实际执行时调用哪个方法,运行时决定
- 重写的时候,不要改变原方法的本意
- 运行阶段进行动态检查
- 父类型中的被重写函数体
- 不为空:
- 该方法是可以被直接复用的
- 对某些子类型来说,有特殊性,可重写父类型中的函数,实现自己的特殊要求
- 为空:
- 其所有子类型都需要这个功能
- 但各有差异,没有共性,在每个子类中均需要重写
- 不为空:
super
- 重写之后,利用
super()
复用了父类型中函数的功能,还可以对其进行扩展 - 如果是在构造方法中调用父类的构造方法,则必须在构造方法的第一行调用
super()
严格继承:子类只能添加新方法,无法重写超类中的方法(方法带
final
关键字)
多态(Polymorphism) & 重载(Overload)
重载:多个方法具有同样的名字,但有不同的参数列表或返回值类型。
Override和Overload
特殊多态:功能重载
- 方便client调用:client可用不同的参数列表,调用同样的函数
- 根据参数列表进行最佳匹配
public void changeSize(int size, String name, float pattern) {}
- 重载函数错误情况
public void changeSize(int length, String pattern, float size) {}
:虽然参数名不同,但类型相同public boolean changeSize(int size, String name, float pattern) {}
:参数列表必须不同
- 在编译阶段时决定要具体执行哪个方法(与之相反,overridden methods则是在run-time进行dynamic checking)
- 可以在同一个类内重载,也可在子类中重载
参数化多态:使用泛型
?
编程子类型多态:期望不同类型的对象可以统一处理而无需区分,遵循LSP原则
3.5 ADT和OOP中的等价性
不可变对象的引用等价性 & 对象等价性
==
引用等价性
相同内存地址
对于:基本数据类型equals()
对象等价性
对于:对象类型在自定义ADT时,需要用
@Override
重写Object.equals()
(在Object中实现的缺省equals()是在判断引用等价性)如果用
==
,是在判断两个对象身份标识 ID是否相等(指向内存里的同一段空间)
equals()
& hashCode()
equals()
的性质:自反、传递、对称、一致性
equals()
重写范例- 判断引用等价性
- 判断类的一致性
- 判断具体值是否满足等价条件(自定义)
instanceof
:
- 判断类
- 仅在equals里使用
hashCode()
:
- 等价的对象必须有相同的
hashCode
- 不相等的对象,也可以映射为同样的
hashCode
,但性能会变差 - 自定义ADT要重写
hashCode
- 返回值是内存地址
可变对象的观察等价性 & 行为等价性
- 观察等价性: 在不改变状态的情况下,两个mutable对象是否看起来一致
- 行为等价性:调用对象的任何方法都展示出一致的结果
对可变类型来说,往往倾向于实现严格的观察等价性, 但在有些时候,观察等价性可能导致bug,甚至可能破坏RI。
边栏推荐
猜你喜欢
MNIST Handwritten Digit Recognition - Lenet-5's First Commercial Grade Convolutional Neural Network
管道重定向
Question 1000: Input two integers a and b, calculate the sum of a+b, this question is multiple sets of test data
安装pyspider后运行pyspider all后遇到的问题
使用JS在浏览器中打印菱形
Copy Siege Lion's Annual "Battle" | Review 2020
淘宝分布式文件系统存储(二)
strlen 转义字符
位段-C语言
[Copy Siege Lion Log] Flying Pulp Academy Intensive Learning 7-Day Punch Camp-Study Notes
随机推荐
【C语言】数组名是什么
PS像素画学习-1
Usage of SFTP
集合--LinkedList
Tencent and NetEase have taken action one after another. What is the metaverse that is so popular that it is out of the circle?
第一章 绪论
[开发杂项][编辑器][代码阅读]ctags&vim
file editor
MySQL索引
指针的运算【C语言】
【五一专属】阿里云ECS大测评#五一专属|向所有热爱分享的“技术劳动者”致敬#
树和二叉树
基于Webrtc和Janus的多人视频会议系统开发6 - 从Janus服务器订阅媒体流
Pytest common plug-in
Code to celebrate the Dragon Boat Festival - Zongzi, your heart
第三章 标准单元库(上)
Shell脚本执行的三种方式
The usefulness of bind() system call
Rules.make - suitable for viewing in edit mode
LeetCode_22_Apr_2nd_Week