第一节 多维视图和质量目标
软件构造多维度视图
红色标注为重点(考试会考选择题)
Moment 特定时刻的软件形态 Period 软件形态随时间的变化
AST (Abstract Syntax Tree) 抽象语法树
SCI (Software Configuration Item) 配置项
concurrent multithreads 并发多线程
下面绿色的部分表示是由用户输入等引起的,是可预测的,在程序运行时处理
不需要实例化Error,也不需要捕获(捕获了也处理不了)
异常分为:运行时异常(RuntimeException)和其他异常
运行时异常是程序员代码里处理不当造成,其他异常由外部原因造成
Checked and unchecked exceptions
Unchecked exceptions = Error + RuntimeExceptions
两者区分:编译器是否能检查出(编译器不会检查Unchecked exception)
checked exception 必须捕获并指定错误处理器handler,否则编译无法通过
五个处理异常时使用的关键字:try,catch,finally,throws,throw
Unchecked异常也能用try/catch来进行捕获,但大多数时时不需要的,也不应该这样做——掩耳盗铃,对发现的编程错误充耳不闻!
尽量用unchecked exception来处理编程错误——使代码更易读
错误可预料,不可预防,但有手段从中恢复,用checked exception
- 工作区,即平时存放代码的地方。
- 暂存区,虚拟区域,无真实空间,用于临时存放改动。
- 本地仓库,安全存放数据的位置,这里有提交到所有版本的数据
- 远程仓库,托管代码的服务器,如Github
需要掌握根据Git文件的状态来判断处于哪一目录/区域中。
Object Graph
边 A -> B表示在版本B的基础上作变化形成了版本A(指向的对象是父对象)
- 除了最初的commit,每个commit都有一个指向父亲的指针
- 多个commit指向同一个父亲——分支
- 一个commit指向两个父亲——合并
Git中一个子对象只能有0,1,2个父对象,而一个父对象可以有多个子对象。
Git和传统版本控制工具的区别:Git存储的是变化后的文件,传统VCS存储版本之间的变化(行),很难创建分支。
Git一个文件可以存在在不同的版本中。
Git命令和版本图
git commit -a :把所有的change先add然后再commit
git fetch :从远程获取最新版本到本地,不会自动merge
git checkout -b:创建并切换分支
git remote add origin … :与远程仓库关联
注:git会强制在push之前fetch(如果远程端做了更改),然后再merge和push
第四节 数据类型和类型检验
基本数据类型/对象数据类型
第六节 抽象数据类型(ADT)
ADT 的特性:表示泄漏、抽象函数 AF 、表示不变量 RI
基于数学的形式对 ADT 的这些核心特征进行描述并应用于设计中
(本节常考大题,包括是否出现表示暴露,AF和RI等)
ADT是由操作定义的,与其内部实现无关
ADT四种操作类型
- 构造器(Creator):创建一个该类型的新对象(可能实现为构造函数或静态函数)
- 生产器(Producer):从一个类型的旧对象创建一个新对象(如String中concat方法)
- 观察器(Observer):返回一个不同类型的对象(如List中的size方法)
- 变值器(Mutator):改变对象属性的方法(如List中的add方法)
上述实例就违反了表示独立性,因为public限定了这是一个instance variable,但是后面的final又限定了客户端不能够实际改变这个类的immutability属性
*不变性(Invariants)
由ADT来负责其不变量,与client端的任何行为无关
(考试必考表示泄露)
表示泄露出现情况:
- public 类型的数据 -> private final
- mutable 类型共享引用
- 不应该包含mutate方法
当复制代价很高时,可在规约中强加条件(但是不推荐!)
*Rep Invariant and Abstraction Function(RI and AF)
R : 表示空间 A:抽象空间(ADT开发者关注表示空间R,client关注抽象空间A)
可用ADT的不变量来代替前置条件(相当于将复杂的precondition封装到了ADT内部)
第七节 面向对象的编程(OOP)
静态/实例方法
在类中使用static修饰的静态方法会随着类的定义而被分配和装载入内存中;而非静态方法属于对象的具体实例,只有在类的对象创建时在对象的内存中才有这个方法的代码段
编译器只为整个类创建了一个静态变量的副本,也就是只分配一个内存空间,虽然可能有多个实例,但这些实例共享该内存
接口(Interface)
接口之间可以继承与扩展,一个类可以实现多个接口,一个接口可以有多种实现类
接口:确定ADT规约; 类:实现ADT
Java的接口中不能含有constructors,但是从Java 8开始接口中可以含有static工厂方法,可用其替代constructors
default
通过default方法,可以在接口中统一实现某些功能,无需在各个类中重复实现它。好处是以增量式为接口增加额外的功能而不破坏已经实现的类
参数多态和泛型(Generic)
泛型擦除:运行时泛型类型消除(如:运行时是不知道String的),所以,不能使用泛型数组(如: Pair [] foo = new Pair [42]; 是错误的!不能被编译!)
如下是一个错误的实例:
通配符(Wildcards),只在使用泛型的时候出现,不能在定义中出现。 如:List
xtends T 和 uper T 分别表示T和它的所有子/父类
子类型多态、继承
- 重写时,子类的规约要强于父类的规约(更弱的前置条件,更强的后置条件)
- 子类的可见性要强于父类(即父类如果是public,子类不能为private)
- 子类不能比父类抛出更多的异常
(详情见LSP原则)
注:Java无法检测1,但是可以检测出2、3
子类型多态:不同类型的对象可以统一的处理而无需区分。
instanceof
判断对象运行时的类型
注:其父类也会判为true,如 始终为true
获取当前类型
- 不是的父类
- 是的父类
- 是 的父类
注:重写方法时,需要注意参数类型,必须也是类型
第八节 ADT和OOP中的相等性
相等关系
相等关系是一种等价关系,即满足自反、对称、传递
可以用”是否为等价关系”来检验equals()是否正确
Immutable类型的相等
判相等要从A空间来看(用户角度) AF映射到相同结果,则等价
站在外部观察者角度:对两个对象调用任何相同的操作,都会得到相同的结果,则认为这两个对象是等价的。
== vs. equals()
- 表示的是引用等价性(一般用于基本数据类型的相等判定)
- 表示的是对象等价性 (用于对象类型相等判定)
在自定义ADT时,需要重写Object 的 equals() 方法
equals()方法的实现
在Objects中实现的缺省equals()是在判断引用相等性(相当于==)
用操作可以判断对象是否是一种特殊的类型(用instanceof是一种动态类型检查,而不是静态类型检查)
注意:不能在父类中用instanceof判断子类类型
- 等价的对象必须拥有相同的hashCode;不相等的对象也可以映射为同样的hashCode,但是性能会变差
- 重写equals方法必须要重写hashCode方法(除非能保证你的ADT不会被放入到Hash类型的集合中)
mutable类型的相等
观察等价性:在不改变状态的情况下,两个mutable对象是否看起来一致
行为等价性:调用对象的任何方法都展示出一致的结果
对于mutable类型来说,往往倾向于实现严格的观察等价性(但是在有些时候,观察等价性可能导致bug,甚至破坏RI)
注意:如果某个mutable的对象包含在Set集合类中,当其发生改变后,集合类的行为不确定!
Collections 使用的是观察等价性,但是其他的mutable类(如StringBuilder)使用的是行为等价性
flase
(Numbers between -128 and 127 are true.)
第九节 面向复用的软件构造技术
(重点:LSP和组合与委托)(考试大部分为小题,LSP会出大题)
复用的类型
软件复用:最主要的是代码复用,但也有其他方面。
Source code level:methods, statements, etc Module level:class and interface Library level:API Architecture level:framework
白盒复用:源代码可见、可修改和扩展(对应继承)
黑盒复用:源代码不可见,不能修改,只能通过API接口使用(对应委托)
代码复用
即直接复制代码(不推荐)
类的复用
inheritance 继承 delegation 委托
继承能做的事委托也能做,继承要求严格父子关系
框架(framework)
框架:一组具体类、抽象类、及其之间的连接关系
开发者根据framework的规约,填充自己的代码进去,形成完整系统
API和框架的区别:主控端在用户/框架
*LSP(重点)
子类型多态:客户端可以用统一的方式处理不同类型的对象。
LSP原则
能被Java静态类型检测检测出的:
- 子类型可以增加方法,但不可以删除方法
- 子类型需要实现抽象类型中的所有未实现的方法
- 子类型中重写的方法必须返回相同的类型或者子类型(满足协变)
- 子类型中重写的方法必须使用同样类型的参数(或符合逆变的参数)
- 子类型中重写的方法不能抛出额外的异常(协变)
不能被静态类型检测出的:
- 更强的不变量
- 更弱的前置条件
- 更强的后置条件(与上条综合即规约更强)
协变:父类型->子类型:越来越具体
反协变(逆变):越来越抽象
注意:Java不支持反协变!Java识别其为重载(而非重写)
数组满足协变。
泛型中的LSP
泛型不满足协变 不是的子类型
Object是所有泛型的父类,是的父类
另一种方法:让ADT实现Comparable接口,然后override compareTo() 方法
与使用Comparator的区别:不需要构建新的Comparator类,比较代码放在ADT内部
耦合和内聚之间的权衡:
声明:本站部分文章及图片源自用户投稿,如本站任何资料有侵权请您尽早请联系jinwei@zod.com.cn进行处理,非常感谢!