一、计算机系统
计算机基本单位
单位 | 名称 | 简称 | 换算 |
---|---|---|---|
位 | bit | b | |
字节 | byte | B | 1B=8b |
千字节 | KB | 1KB=1024B | |
兆字节 | MB | 1MB=1024KB | |
吉字节 | GB | 1GB=1024MB | |
太字节 | TB | 1TB=1024GB |
带宽单位Mbps的b是指Bit(位)
速度单位MB/s的B是指Byte(字节)
1MB/s=8Mbps
2的次方对照表
2^0=1
2^1=2
2^2=4
2^3=8
2^4=16
2^5=32
2^6=64
2^7=128
2^8=256
2^9=512
2^10=1024
2^11=2048
2^12=4096
2^13=8192
硬件
CPU的组成
CPU分为运算器与控制器两大部分
运算器
算术逻辑单元ALU:执行算术运算和逻辑运算。
累加寄存器AC:暂存数据,为ALU提供工作区。
数据缓冲寄存器DR
状态条件寄存器PSW
控制器
程序计数器PC:存储下一条要执行指令的地址,程序员可访问
指令寄存器IR:存储即将执行的指令,还有计数的功能,程序员无法访问
地址寄存器AR:存放的是CPU访问内存单元的地址
指令译码器ID:是把操作码解析成对应的指令操作
原码、反码、补码、移码
原码和反码的取值范围:-127~127
补码和移码的取值范围:-128~127
浮点数
先对阶,小阶向大阶对齐,浮点数尾数向右移
浮点数所能表示的数值范围主要由阶码决定,所表示数值的精度则由尾数决定
寻址
CPU访问速度:
CPU内部通用寄存器>Cache>主存储器>联机磁盘存储器>脱机光盘磁盘存储器
奇偶校验码
只能检错,不能纠错
码距=2,检错能力
海明码
码距≥3,才可能有纠错能力
数据位是n位,校验位是k位,则n和k必须满足以下关系:
2^k-1≥n+k
循环冗余校验码
码距=2,运用模二运算进行检错不能纠错
RISC与CISC
流水线
指令流水线:第1条指令的执行时间+(n-1)*(最长时间段)
不采用流水线/采用流水线 = 加速比
操作周期:最长时间段
吞吐率:最长时间段的倒数
执行n条指令的吞吐率:n/最长时间段
存储器
相联存储器:按内容访问的存储器
虚拟存储器:由主存、辅存构成
CPU:空间局部性、时间局部性
Cache地址映像
直接映像
主存的块与Cache块的对应关系是固定的。
特点:冲突多。③
全相联映像
允许主存的任一块可以调入Cache存储器的任何一个块的空间中。
特点:冲突少。①
组相联映像
特点:冲突较少。②
中断
中断向量:提供中断服务程序的入口地址
中断向量表:所有中断服务的入口地址
中断响应时间:从发出中断请求到进入中断服务程序
保存现场继续执行主程序
输入输出(IO)控制方式
程序查询方式(串行)
①CPU和I/O(外设)只能串行工作
CPU需要一直轮询检查,长期处于忙等状态。CPU利用率低
②一次只能读/写一个字(8bit)
③由CPU将数据放入内存
中断驱动方式(并行)
①I/O设备通过中断信号主动向CPU报告I/O操作已完成
②CPU和I/O(外设)可并行工作
③CPU利用率得到提升
④一次只能读/写一个字(8bit)
⑤由CPU将数据放入内存
直接存储器方式(DMA,并行)
①CPU和I/O(外设)可并行工作
②仅在传送数据块的开始和结束时才需要CPU的干预
③由外设直接将数据放入内存(主存)
④一次读写的单位为“块”而不是字
加密技术与认证技术
明文会出现的问:窃听、篡改、假冒、否认
解决方法:窃听用加密、篡改用摘要、假冒、否认用数字签名
对称加密与非对称加密
1、对称加密(私有密钥加密)
加密和解密是同一把密钥
只有一把密钥
密钥分发有缺陷
①加密解密速度很快
②适合加密大量明文数据
2、非对称加密(公开密钥加密)
加密和解密不是同一把密钥
一共有两把密钥
分别是公钥和私钥
用公钥加密只能用私钥解密
用私钥加密只能用公钥解密
不能通过一把推出另一把
用接收方的公钥加密明文
可以实现防止窃听的效果
密钥分发没有缺陷
①加密解密速度很慢
摘要与数字签名
摘要:将发送的明文进行Hash算法后得到摘要,放在密文后一起发送过去,与接收方解密后的明文进行相同的Hash算法得到的摘要进行对比,如果一致,则没有篡改、否则有篡改。
数字签名:发送方用自己的私钥对摘要进行签名(加密),得到数字签名放在密文后一起发送过去。
数字证书
CA:权威机构
数字证书:
1、用户向CA机构申请数字证书
2、将个人信息和公钥发给CA机构
3、CA机构颁给用户数字证书
4、数字证书用CA的私钥进行签名(加密)
5、用CA的公钥验证(解密)数字证书
6、得到用的公钥
认证处理主动攻击。
公钥加密认证。
私钥解密签名,数字签名确保信息的真实性。
加密算法
对称密钥(私钥、私有密钥加密)算法
(共享密钥加密算法)
1、DES
2、3DES
3、RC-5
4、IDEA
5、AES
6、RC4
非对称密钥(公钥、公开密钥加密)算法
1、RSA
2、ECC
3、DSA
可靠性
1、串联系统
R=R1R2...RN
2、并联系统
R=1-(1-R1)(1-R2)...(1-RN)
与、或、同或、异或
与(AND)&
全1为1,有0为0
或(OR)|
全0为0,有1为1
异或(XOR)^
相同为0,不同为1
同或(XNOR)
相同为1,不同为0
二、程序设计语言
编译程序和解释程序
解释器
翻译时不生成独立的目标程序,解释程序和源程序都参与程序运行过程
编译器
翻译时独立生成目标程序,源程序和编译程序不再参与目标程序的运行过程
编译、解释与翻译阶段
编译器方式中间代码生成和代码优化不是必要的,可省略
即在词法分析、语法分析、语义分析阶段后直接生成目标代码
编译过程
源程序→词法分析→记号流→词法分析→分析树(语法树)→语义分析
词法分析
词法分析阶段的主要作用是分析构成程序的字符及
由字符按照构造规则构成的符号是否符合程序语言的规定
语法分析
语法分析阶段的主要作用是
对各条语句的结构进行合法性分析
分析程序中的句子结构是否正确
语法分析阶段可以发现程序中的所有语法错误
语义分析
语义分析阶段的主要作用是进行类型分析和检查
语义分析阶段不能发现程序中所有的语义错误
语义分析阶段可以发现静态语义错误
不能发现动态语义错误,动态语义错误运行时才能发现
传值调用与传址调用
传值调用
将实参的值传递给形参,实参可以是变量、常量和表达式。
不可以实现形参和实参间双向传递数据的效果。
传引用(地址)调用
将实参的地址传递给形参,形参必须有地址,实参不能是常量(值)、表达式。
可以实现形参和实参间双向传递数据的效果,即改变形参的值同时也改变了实参的值。
中间代码生成
常见的中间代码有:后缀式、三地址码、三元式、四元式和树(图)等形式。
上下文无关文法
大多数程序设计语言的语法规则用上下文无关文法描述
中缀、后缀表达式转换
优先级:1、();2、*、/;3、+、-
?: +-*/
中缀:a?b
后缀:ab?
语法树中、后序遍历
中序:左根右
后序:左右根
三、知识产权
著作权(版权)
发表权受时间限制,署名权、修改权、保护作品完整权永远属于作者。
专利地域性
在哪申请,就在哪受保护
计算机软件著作权(50年)
计算机软件著作权受到《中华人民共和国著作权法》和《计算机软件保护条例》这两个法律的保护
职务作品
只享有署名权,其他权归公司所有
委托开发
有合同,按合同约定,无合同,著作权由受委托人享有
专利申请
先申请先得,同一天申请协商
商标权
10年内有效,可无限延长
商标注册
先注册先得,同一天注册先使用先得,同一天注册都没使用过协商
四、数据库
三级模式结构
概念模式(基本表)
外模式(视图)
内模式(存储文件)
两级映像
模式/内模式映像
数据的物理独立性:为了保证应用程序能够正确执行,需要修改概念模式和内模式之间的映像。
外模式/模式映像
数据的逻辑独立性:为了保证应用程序能够正确执行,需要修改外模式和概念模式之间的映像。
范式
第一范式(1NF,属性原子化)
消除非主属性对码的部分函数依赖
部分函数依赖导致数据冗余、更新异常
第二范式(2NF,消除部分函数依赖)
消除非主属性对码的传递函数依赖
关系模式分解消除部分函数依赖
传递函数依赖导致数据冗余、更新异常
第三范式(3NF,消除传递函数依赖)
消除主属性对码的部分和传递函数依赖
BC范式(BCNF,消除主属性对候选码的部分和传递依赖)
消除非平凡且非函数依赖的多值依赖
第四范式(4NF)
数据库设计
数据库设计策略:自顶向下、自底向上
数据库设计的步骤,简称新奥尔良法:
1、用户需求分析。
2、概念设计。
概念结构设计是对信息分析和定义,描述概念模型较理想的是采用E-R方法。
3、逻辑设计。
4、物理设计。
逻辑模型在计算机中的具体实现方案。
需求分析阶段
成果:系统需求说明书,主要包括数据流图、数据字典、各种说明性表格、统计输出表和系统功能结构图。
概念结构设计阶段
1、选择局部应用
2、逐一设计分E-R图
3、E-R图合并
1、属性冲突
2、命名冲突
3、结构冲突
逻辑结构设计
1、实体向关系模式的转换
2、联系向关系模式的转换
事务管理
1、原子性:事务是原子的,要么都做,要么都不做。
2、一致性:事务执行的结果必须保证数据库从一个一致性状态变到另一个一致性状态。因此,当数据库只包含成功事务提交的结果时,称数据库处于一致性状态。
3、隔离性:事务相互隔离。当多个事务并发执行时,任一事务的更新操作直到其成功提交的整个过程,对其他事务都是不可见的。
4、持久性:一旦事务成功提交,即使数据库崩溃,其对数据库的更新操作也将永久有效。
数据库备份与恢复
备份方法
1、静态转储和动态转储。
2、海量转储和增量转储。
3、日志文件。
恢复
1、反向扫描文件日志(即从最后向前扫描日志文件),查找该事务的更新操作。
2、对事务的更新操作执行逆操作。
3、继续反向扫描日志文件,查找该事务的其他更新操作,并做同样的处理,直到事务的开始标志。
封锁
并发控制的主要技术是封锁
基本封锁的类型:排它锁(X锁或写锁)和共享锁(S锁或读锁)
分布式数据库
五、面向对象
对象和消息
对象
一个对象把属性和行为封装为一个整体
对象是一个程序模块
一个对象通常可由对象名、属性、方法3个部分组成
消息
对象之间进行通信的一种构造叫作消息
方法重载
方法名相同(同名),但是参数个数或者参数类型不同叫方法重载
封装
封装是一种信息隐藏的技术。其目的是使对象的使用者和生产者分离
对象是封装数据和行为的整体
关键字this可以用于区分同名的对象属性和局部变量名
继承
继承是父类和子类之间共享数据和方法的机制
只从一个父类得到继承,叫作“单重继承”
如果一个子类有两个或更多个父类,则称为“多重继承”
多态
在收到消息时,对象要予以响应
不同的对象收到同一消息可以产生完全不同的结果,这一现象称为多态
多态的实现受到继承的支持
参数多态
应用比较广泛的多态,被称为最纯的多态
包含多态
在许多语言中都存在,最常见的例子就是子类型化,即一个类型是另一个类型的子类型
过载多态
同一个名字在不同的上下文所代表的含义不同
强制多态
面向对象设计原则
单一责任原则
开放-封闭原则
里氏替换原则
依赖倒置原则
接口分离原则
共同封闭原则
共同重用原则
面向对象分析与设计
面向对象分析
面向对象分析目的是为了获得对应用问题的理解
包含5个活动:
1、认定对象
2、组织对象
3、描述对象间的相互作用
4、确定对象的操作
5、定义对象的内部信息
面向对象设计
包含5个活动:
1、识别类及对象
2、定义属性
3、定义服务
4、识别关系
5、识别包
六、UML
UML4种关系
依赖关系
1、一个事物发生变化会影响另一个事物的语义
2、类的方法中定义了另外一个类的对象,那是这个类依赖另外一个类
3、一个类需要用到什么那就是依赖
实现关系
1、在接口和实现它们的类或构件之间
2、在用例和实现它们的协作之间
泛化关系
1、特殊/一般关系,特殊元素(子元素)的对象可替代一般元素(父元素)的对象。
关联关系
1、是一种结构关系,描述了一组链,链是对象之间的连接
聚集:是一种特殊类型的关联,它描述了整体和部分间的结构关系
UML图
类图
展现了一组对象、接口、协作和它们之间的关系
类图通常包括下述内容:
1、类
2、接口
3、协作
4、依赖、泛化和关联关系
对系统的静态设计视图建模时:
1、对系统的词汇建模
2、对简单的协作建模
3、对逻辑数据库模式建模
对象图
展现了某一时刻一组对象以及它们之间的关系
对象图一般包括:对象和链
用例图
用例图展现了一组用例、参与者以及它们之间的关系
用例图通常包括下述内容:
1、用例
2、参与者
3、用例之间的扩展关系(<<extend>>
)和包含关系(<<include>>
),参与者和用例之间的关联关系,用例与用例以及参与者与参与者之间的泛化关系。
场景:
1、对系统的语境建模
2、对系统的需求建模
包含关系
包含关系描述的是一个用例需要某种功能,而该功能被另外一个用例定义,那么在用例的执行过程中,就可以调用已经定义好的用例。表示符号:<<include>>
扩展关系
用一个用例(可选)扩展另一个用例(基本例)的功能,将一些常规的动作放在一个基本用例中,将可选的或只在特定条件下才执行的动作放在它的扩展用例中。表示符号:<<extend>>
泛化关系
泛化是一种特殊/一般关系,特殊元素(子元素)的对象可替代一般元素(父元素)的对象
泛化关系用一条带有空心箭头的实线,它指向父元素
交互图
交互图用于对系统的动态方面进行建模
交互图一般包含对象、链、消息
序列图(顺序图)
多个对象与一个用例的行为
序列图是场景的图形化表示,描述了以时间顺序组织的对象之间的交互活动
序列图有两个不同于通信图的特征:
(1)序列图有对象生命线
对象生命线是一条垂直的虚线,表示一个对象在一段时间内存在
(2)序列图有控制焦点
控制焦点是一个瘦高的矩形,表示一个对象执行一个动作所经历的时间段,矩形的顶部表示动作的开始,底部表示动作的结束
通信图(协作图)
通信图(别名:协作图)强调收发消息的对象的结构组织
通信图有两个不同于序列图的特性:
(1)通信图有路径
(2)通信图有顺序号
沿同一个链可以显示许多消息(可能发自不同的方向),并且每个消息都有唯一的一个顺序号
通信图展现了对象之间的消息流及其顺序
序列图和通信图是同构的,它们之间可以相互转换
状态图
状态图展现了一个状态机,它由状态、转换、事件和活动组成
强调对象行为的事件顺序
状态图对系统的动态方面建模
当对系统、类或用例的动态方面建模时,通常是对反应型对象建模
状态
状态是任何可以被观察到的系统行为模式,一个状态代表系统的一种行为模式
状态图的状态主要有:初态(即初始状态)、终态(即最终状态)和中间状态
转换和事件
是在某个特定时刻发生的事情,它是对引起系统做动作或(和)从一个状态转换到另一个状态的外界事件的抽象
在表示状态转换的箭头线上标出触发转换的事件表达式
如果箭头线上未标明事件,则表示在源状态的内部活动执行完之后自动触发转换
表达式:事件说明[监护条件]/动作表达式
活动图
活动图是一种特殊的状态图,它展现了在系统内从一个活动到另一个活动的流程。
包括活动状态和动作状态。转换和对象。
活动图可以表示分支、合并、分岔和汇合。
两种活动图的方式:
1、对工作流建模
2、对操作建模
构件图(组件图)
构件图展现了一组构件之间的组织和依赖。构件图专注于系统的静态实现视图。
通常把构件映射为一个或多个类、接口或协作。
部署图
展现了系统的软件与硬件之间的关系
场景:
1、在实施阶段使用
UML图总结
静态建模
类图、对象图、用例图
动态建模
序列图(顺序图,时序图)、通信图(协作图)、状态图、活动图
物理建模
构件图(组件图)、部署图
交互图
序列图(顺序图,时序图)、通信图(协作图)
图 | 关系 |
---|---|
类图 | 一组对象、接口、协助和它们之间的关系 |
对象图 | 某一时刻一组对象之间的关系 |
用例图 | 一组用例与参与者之间的关系 |
序列图 | 多个对象与一个用例的行为 |
通信图 | 对象之间的消息流及顺序 |
状态图 | |
活动图 | 一个活动到另一个活动的流程 |
构件图 | 一组构件之间的组织及依赖 |
部署图 | 软件与硬件的关系 |
七、设计模式
创建型设计模式
简单工厂模式
简单工厂模式属于创建型模式,但不属于23种设计模式之一,
定义:定义一个工厂类,他可以根据参数的不同返回不同类的实例,被创建的实例通常都具有共同的父类。
三类角色:
工厂(核心):负责实现创建所有产品的内部逻辑,工厂类可以被外界直接调用,创建所需对象。
抽象产品:工厂类所创建的对象的父类,封装了产品对象的公共方法,所有的具体产品为其子类对象。
具体产品:简单工厂模式的创建目标,所有被创建的对象都是某个具体类的实例。它要实现抽象产品中声明的抽象方法。
简单工厂模式代码
public class SimpleFactory {
public static void main(String[] args) {
Product productA = Factory.createProduct("A");
productA.info();
Product productB = Factory.createProduct("B");
}
}
abstract class Product {
public abstract void info();
}
class Factory {
public static Product createProduct(String type) {
Product product = null;
switch (type) {
case "A":
product = new ProductA();
break;
case "B":
product = new ProductB();
break;
default:
System.out.println("没有 " + type + " 类型的产品!");
break;
}
return product;
}
}
class ProductA extends Product {
@Override
public void info() {
System.out.println("产品的信息:A");
}
}
class ProductB extends Product {
@Override
public void info() {
System.out.println("产品的信息:B");
}
}
工厂方法模式(Factory Method)
意图
定义一个用于创建对象的接口,让子类决定实例化哪一个类。Factory Method使一个类的实例化延迟到其子类。
结构
1、Product定义工厂方法所创建的对象的接口。
2、ConcreteProduct实现Product接口。
3、Creator声明工厂方法,该方法返回一个Product类型的对象。
4、ConcreteCreator重定义工厂方法返回一个ConcreteProduct实例。
适用性
1、当一个类不知道它所必须创建的对象的类的时候。
2、当一个类希望由它的子类来指定它所创建的对象的时候。
3、(了解即可)当类将创建对象的职责委托给多个帮助子类中的某一个,并且你希望将哪一个帮助子类是代理者这一信息局部化的时候。
工厂方法模式代码
public class FactoryMethod {
public static void main(String[] args) {
Factory factoryA = new FactoryA();
// 父类 对象名 = new 子类();
Product productA = factoryA.createProduct();
// Product productA = new ProductA();
productA.info();
Factory factoryB = new FactoryB();
Product productB = factoryB.createProduct();
productB.info();
}
}
// class Factory
interface Factory {
public Product createProduct();
}
class FactoryA implements Factory {
@Override
public Product createProduct() {
return new ProductA();
}
}
class FactoryB implements Factory {
@Override
public Product createProduct() {
return new ProductB();
}
}
// abstract class Product
interface Product {
// public abstract void info();
public void info();
}
// class ProductA extends Product
class ProductA implements Product {
@Override
public void info() {
System.out.println("产品的信息:A");
}
}
// class ProductB extends Product
class ProductB implements Product {
@Override
public void info() {
System.out.println("产品的信息:B");
}
}
抽象工厂模式(Abstract Factory)
意图
提供一个创建一系列相关或相互依赖对象的接口,而无须指定它们具体的类。
结构
1、AbstractFactory声明一个创建抽象产品对象的操作接口。
2、ConcreteFactory实现创建具体产品对象的操作。
3、AbstractProduct为一类产品对象声明一个接口。
4、ConcreteProduct定义一个将被相应的具体工厂创建的产品对象,实现AbstractProduct接口。
5、Client仅使用由AbstractFactory和AbstractProduct类声明的接口。
适用性
1、一个系统要独立于它的产品的创建、组合和表示时。
2、一个系统要由多个产品系列中的一个来配置时。
3、当要强调一系列相关的产品对象的设计以便进行联合使用时。
4、当提供一个产品类库,只想显示它们的接口而不是实现时。
抽象工厂模式代码
public class AbstractFactory {
public static void main(String[] args) {
Factory factory1 = new Factory1();
ProductA productA = factory1.createProductA();
productA.info();
Factory factory2 = new Factory2();
ProductB productB = factory2.createProductB();
productB.info();
}
}
interface Factory {
public ProductA createProductA();
public ProductB createProductB();
}
class Factory1 implements Factory {
@Override
public ProductA createProductA() {
return new ProductA1();
}
@Override
public ProductB createProductB() {
return new ProductB1();
}
}
class Factory2 implements Factory {
@Override
public ProductA createProductA() {
return new ProductA2();
}
@Override
public ProductB createProductB() {
return new ProductB2();
}
}
interface ProductA {
public void info();
}
class ProductA1 implements ProductA {
@Override
public void info() {
System.out.println("产品的信息:A1");
}
}
class ProductA2 implements ProductA {
@Override
public void info() {
System.out.println("产品的信息:A2");
}
}
interface ProductB {
public void info();
}
class ProductB1 implements ProductB {
@Override
public void info() {
System.out.println("产品的信息:B1");
}
}
class ProductB2 implements ProductB {
@Override
public void info() {
System.out.println("产品的信息:B2");
}
}
生成器模式(Builder)
意图
将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
结构
1、Builder为创建一个Product对象的各个部件指定抽象接口。
2、ConcreteBuilder实现Builder的接口以构造和装配该产品的各个部件,定义并明确它所创建的表示,提供一个检索产品的接口。
3、Director构造一个使用Builder接口的对象。
4、Product表示被构造的复杂对象。ConcreteBuilder创建该产品的内部表示并定义它的装配过程。
适用性
1、当创建复杂对象的算法应该独立于该对象的组成部分以及它们的装配方式时。
2、当构造过程必须允许被构造的对象有不同的表示时。
生成器模式代码
import java.util.*;
public class Main {
public static void main(String[] args) {
Director director = new Director();
Builder builder1 = new Builder1();
director.Construct(builder1);
Product product1 = builder1.getResult();
product1.show();
Builder builder2 = new Builder2();
director.Construct(builder2);
Product product2 = builder2.getResult();
product2.show();
}
}
class Director {
public void Construct(Builder builder) {
builder.BuildPart();
}
}
abstract class Builder {
public abstract void BuildPart();
public abstract Product getResult();
}
class Builder1 extends Builder {
Product product = new Product();
@Override
public void BuildPart() {
product.Add("A");
product.Add("B");
product.Add("C");
product.Add("D");
product.Add("E");
product.Add("F");
}
@Override
public Product getResult() {
return product;
}
}
class Builder2 extends Builder {
Product product = new Product();
@Override
public void BuildPart() {
product.Add("A");
product.Add("B");
product.Add("C");
}
@Override
public Product getResult() {
return product;
}
}
class Product {
List<String> parts = new ArrayList<String>();
public void Add(String part) {
parts.add(part);
}
public void show() {
System.out.print("产品的组成:");
for (String s : parts)
System.out.print(s + " ");
System.out.print("\n");
}
}
原型模式(Prototype)
意图
用原型实例指定创建对象的种类,并且通过复制这些原型创建新的对象。
结构
1、Prototype声明一个复制自身的接口。
2、ConcretePrototype实现一个复制自身的操作。
3、Client让一个原型复制自身从而创建一个新的对象。
适用性
1、当一个系统应该独立于它的产品创建、构成和表示时。
2、当要实例化的类是在运行时刻指定时。
3、为了避免创建一个与产品类层次平行的工厂类层次时。
4、当一个类的实例只能有几个不同状态组合中的一种时。建立相应数目的原型并克隆它们,可能比每次用合适的状态手工实例化该类更方便一些。
原型模式代码
public class Main {
public static void main(String[] args) {
Product product1 = new Product(2022, 5.28);
System.out.println(product1.getId() + " " + product1.getPrice());
// Product product2 = new Product(2022, 5.28);
Product product2 = (Product) product1.Clone();
System.out.println(product2.getId() + " " + product2.getPrice());
Product product3 = (Product) product1.Clone();
System.out.println(product3.getId() + " " + product3.getPrice());
}
}
interface Prototype {
public Object Clone();
}
class Product implements Prototype {
private int id;
private double price;
public Product() {}
public Product(int id, double price) {
this.id = id;
this.price = price;
}
public int getId() {
return id;
}
public double getPrice() {
return price;
}
@Override
public Object Clone() {
Product object = new Product();
object.id = this.id;
object.price = this.price;
return object;
}
}
单例模式(Singleton)
意图
保证一个类仅有一个实例,并提供一个访问它的全局访问点。
结构
1、Singleton指定一个Instance操作,允许客户访问它的唯一实例,Instance是一个类操作;可能负责创建它自己的唯一实例。
适用性
1、当类只能有一个实例而且客户可以从一个众所周知的访问点访问它时。
2、当这个唯一实例应该是通过子类可扩展的,并且客户无须更改代码就能使用一个扩展的实例时。
单例模式代码
public class SingletonPattern {
public static void main(String[] args) {
// Singleton singleton1 = new Singleton();
Singleton singleton1 = Singleton.getInstance();
Singleton singleton2 = Singleton.getInstance();
Singleton singleton3 = Singleton.getInstance();
System.out.println(singleton1.getNumber() + " " + singleton2.getNumber() + " " + singleton3.getNumber());
singleton1.setNumber(528);
System.out.println(singleton1.getNumber() + " " + singleton2.getNumber() + " " + singleton3.getNumber());
}
}
class Singleton {
private int number = 2022;
public void setNumber(int number) {
this.number = number;
}
public int getNumber() {
return number;
}
private static Singleton instance = new Singleton();
private Singleton() {}
public static Singleton getInstance() {
return instance;
}
}
结构型设计模式
结构型设计模式涉及如何组合类和对象以获得更大的结构。
结构型类模式采用继承机制来组合接口或实现。
结构型对象模式不是对接口和实现进行组合,而是描述了如何对一些对象进行组合。
适配器模式(Adapter)
意图
将一个类的接口转换成客户希望的另外一个接口。Adapter模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。
结构
1、Target定义Client使用的与特定领域相关的接口。
2、Client与符合Target接口的对象协同。
3、Adaptee定义一个已经存在的接口,这个接口需要适配。
4、Adapter对Adaptee的接口与Target接口进行适配。
适用性
1、想使用一个已经存在的类,而它的接口不符合要求。
2、(了解即可)想创建一个可以复用的类,该类可以与其他不相关的类或不可预见的类(即那些接口可能不一定兼容的类)协同工作。
3、(了解即可)(仅适用于对象Adapter)想使用一个已经存在的子类,但是不可能对每一个都进行子类化以匹配它们的接口。对象适配器可以适配它的父类接口。
适配器模式代码
public class AdapterPattern {
public static void main(String[] args) {
USB usb = new Adapter();
usb.Request();
}
}
class USB {
public void Request() {
System.out.println("USB数据线");
}
}
class Adapter extends USB {
private TypeC typeC = new TypeC();
@Override
public void Request() {
typeC.SpecificRequest();
}
}
class TypeC {
public void SpecificRequest() {
System.out.println("Type-C数据线");
}
}
桥接模式(Bridge)
意图
将抽象部分与其实现部分分离,使它们都可以独立地变化。
结构
1、Abstraction定义抽象类的接口,维护一个指向Implementor类型对象的指针。
2、RefinedAbstraction扩充Abstraction定义的接口。
3、Implementor定义实现类的接口,该接口不一定要与Abstraction的接口完全一致;
4、ConcreteImplementor实现Implementor接口并定义它的具体实现。
适用性
1、(了解即可)不希望在抽象和它的实现部分之间有一个固定的绑定关系。
2、(了解即可)类的抽象以及它的实现都应该可以通过生产子类的方法加以扩充。
3、(了解即可)对一个抽象的实现部分的修改应对客户不产生影响,即客户代码不必重新编译。
4、(了解即可)有许多类要生成的类层次结构。
5、(了解即可)想在多个对象间共享实现(可能使用引用计数),但同时要求客户并不知道这一点。
桥接模式代码
public class BridgePattern {
public static void main(String[] args) {
Product productA1 = new ProductA();
Product productA2 = new ProductA();
Color red = new Red();
productA1.setName("产品A1");
productA1.setColor(red);
productA1.Operation();
Blue blue = new Blue();
productA2.setName("产品A2");
productA2.setColor(blue);
productA2.Operation();
}
}
abstract class Product {
private String name;
protected Color color;
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setColor(Color color) {
this.color = color;
}
public abstract void Operation();
}
class ProductA extends Product {
@Override
public void Operation() {
color.OperationImp(this.getName());
}
}
interface Color {
public void OperationImp(String name);
}
class Red implements Color {
@Override
public void OperationImp(String name) {
System.out.println(name + ":红色");
}
}
class Blue implements Color {
@Override
public void OperationImp(String name) {
System.out.println(name + ":蓝色");
}
}
组合模式(Composite)
意图
将对象组合成树形结构以表示“部分-整体”的层次结构。Composite使得用户对单个对象和组合对象的使用具有一致性。
结构
1、Component为组合中的对象声明接口;在适当情况下实现所有类共有接口的默认行为;
2、Leaf在组合中表示叶节点对象,叶节点没有子节点;在组合中定义图元对象的行为。
3、Composite定义有子组件的那些组件的行为;存储子组件;在Component接口中实现与子组件有关的操作。
4、Client通过Component接口操纵组合组件的对象。
适用性
1、想表示对象的部分-整体层次结构。
2、希望用户忽略组合对象与单个对象的不同,用户将统一地使用组合结构中所有对象。
组合模式代码
import java.util.*;
public class CompositePattern {
public static void main(String[] args) {
// 父类名 对象名 = new 子类名();
AbstractFile root = new Folder("root");
AbstractFile folderA = new Folder("folderA");
AbstractFile folderB = new Folder("folderB");
AbstractFile fileC = new File("fileC");
AbstractFile fileD = new File("fileD");
AbstractFile fileE = new File("fileE");
root.Add(folderA);
root.Add(folderB);
root.Add(fileC);
folderA.Add(fileD);
folderA.Add(fileE);
print(root);
}
static void print(AbstractFile file) {
file.printName();
List<AbstractFile> childrenList = file.getChildren();
if (childrenList == null) return;
// for (对象类型 对象名 : 遍历对象)
for (AbstractFile children : childrenList) {
// children.printName();
print(children);
}
}
}
abstract class AbstractFile {
protected String name;
public void printName() {
System.out.println(name);
}
public abstract boolean Add(AbstractFile file);
public abstract boolean Remove(AbstractFile file);
public abstract List<AbstractFile> getChildren();
}
class Folder extends AbstractFile {
private List<AbstractFile> childrenList = new ArrayList<AbstractFile>();
public Folder(String name) {
this.name = name;
}
@Override
public boolean Add(AbstractFile file) {
return childrenList.add(file);
}
@Override
public boolean Remove(AbstractFile file) {
return childrenList.remove(file);
}
@Override
public List<AbstractFile> getChildren() {
return childrenList;
}
}
class File extends AbstractFile {
public File(String name) {
this.name = name;
}
@Override
public boolean Add(AbstractFile file) {
return false;
}
@Override
public boolean Remove(AbstractFile file) {
return false;
}
@Override
public List<AbstractFile> getChildren() {
return null;
}
}
装饰模式(Decorator)
意图
动态地给一个对象添加一些额外的职责。就增加功能而言,Decorator模式比生成子类更加灵活。
结构
1、Component定义一个对象接口,可以给这些对象动态地添加职责。
2、ConcreteComponent定义一个对象,可以给这个对象添加一些职责。
3、Decorator维持一个指向Component对象的指针,并定义一个与Component接口一致的接口。
4、ConcreteDecorator向组件添加职责。
适用性
1、在不影响其他对象的情况下,以动态、透明的方式给单个对象添加职责。
2、(了解即可)处理那些可以撤销的职责。
装饰器模式代码
public class DecoratorPattern {
public static void main(String[] args) {
Person zhangsan = new Student("张三");
zhangsan = new DecoratorA(zhangsan);
zhangsan = new DecoratorB(zhangsan);
zhangsan.Operation();
System.out.println("\n=====我是分割线=====");
// 对象链
Person lisi = new DecoratorB(new DecoratorA(new Student("李四")));
}
}
abstract class Decorator extends Person {
protected Person person;
}
class DecoratorA extends Decorator {
public DecoratorA(Person person) {
this.person = person;
}
@Override
public void Operation() { // 职责
person.Operation(); // 原本的职责
System.out.print("写作业 ");
}
}
class DecoratorB extends Decorator {
public DecoratorB(Person person) {
this.person = person;
}
@Override
public void Operation() { // 职责
person.Operation(); // 原本的职责
System.out.print("考试 ");
}
}
abstract class Person {
protected String name;
public abstract void Operation(); // 职责
}
class Student extends Person {
public Student(String name) {
this.name = name;
}
@Override
public void Operation() {
System.out.print(name + "的职责:学习 ");
}
}
外观模式(Facade)
意图
为子系统中的一组接口提供一个一致的界面,Facade模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。
结构
1、Facade知道哪些子系统类负责处理请求;将客户的请求代理给适当的子系统对象。
2、Subsystem classes实现子系统的功能;处理有Facade对象指派的任务;没有Facade的任何相关信息,即没有指向Facade的指针。
适用性
1、要为一个复杂子系统提供一个简单接口时。
2、客户程序与抽象类的实现部分之间存在着很大的依赖性。
3、当需要构建一个层次结构的子系统时,使用Facade模式定义子系统中每层的入口点。
外观模式代码
public class FacadePattern {
public static void main(String[] args) {
Facade facade = new Facade();
facade.methodA();
facade.methodB();
facade.methodC();
}
}
class Facade {
SubSystemOne subSystemOne;
SubSystemTwo subSystemTwo;
SubSystemThree subSystemThree;
public Facade() {
subSystemOne = new SubSystemOne();
subSystemTwo = new SubSystemTwo();
subSystemThree = new SubSystemThree();
}
public void methodA() {
subSystemOne.methodOne();
}
public void methodB() {
subSystemTwo.methodTwo();
}
public void methodC() {
subSystemThree.methodThree();
}
}
class SubSystemOne {
public void methodOne() {
System.out.println("执行子系统一的功能");
}
}
class SubSystemTwo {
public void methodTwo() {
System.out.println("执行子系统二的功能");
}
}
class SubSystemThree {
public void methodThree() {
System.out.println("执行子系统三的功能");
}
}
享元模式(Flyweight)
意图
运用共享技术有效地支持大量细粒度的对象。
结构
1、Flyweight描述一个接口,通过这个接口Flyweight可以接受并作用于外部状态。
2、ConcreteFlyweight实现Flyweight接口,并为内部状态(如果有)增加存储空间。ConcreteFlyweight对象必须是可共享的。它所存储的状态必须是内部的,即它必须独立于ConcreteFlyweight对象的场景。
3、并非所有的Flyweight子类都需要被共享。Flyweight接口使共享成为可能,但它并不强制共享。
4、FlyweightFactory创建并管理Flyweight对象;确保合理地共享Flyweight,当用户请求一个Flyweight时,FlyweightFactory对象提供一个已创建的实例或者不存在时创建一个实例。
5、Client维持一个对Flyweight的引用;计算或存储一个或多个Flyweight的外部状态。
适用性
1、一个应用程序使用了大量的对象。
2、完全由于使用大量的对象,造成很大的存储开销。
3、对象的大多数状态都可变为外部状态。
4、如果删除对象的外部状态,那么可以用相对较少的共享对象取代很多组对象。
5、应用程序不依赖于对象标识。
享元模式代码
import java.util.*;
public class FlyWeightPattern {
public static void main(String[] args) {
ShapeFactory factory = new ShapeFactory();
Random random = new Random();
String[] colors = {"red", "blue", "green", "white", "black"};
for (int i = 1; i <= 100; i ++ ) {
int x = random.nextInt(colors.length); // [0 ~ 4]
Shape shape = factory.getShape(colors[x]);
System.out.print("第" + i + "个圆:");
shape.draw(random.nextInt(2022), random.nextInt(528));
}
}
}
class ShapeFactory {
private Map<String, Shape> map = new HashMap<String, Shape>();
public Shape getShape(String key) {
if (!map.containsKey(key)) {
map.put(key, new Circle(key));
System.out.println("create color:" + key + " circle");
}
return map.get(key);
}
}
abstract class Shape {
protected String color;
public abstract void draw(int x, int y);
}
class Circle extends Shape {
public Circle(String color) {
this.color = color;
}
@Override
public void draw(int x, int y) {
System.out.println("draw a color:" + color + " circle x:" + x + " y:" + y);
}
}
代理模式(Proxy)
意图
为其他对象提供一种代理以控制对这个对象的访问。
结构
Proxy保存一个引用使得代理可以访问实体;提供一个与Subject的接口相同的接口,使代理可以用来代替实体;控制对实体的存取,并可能负责创建和删除它;
Subject定义RealSubject和Proxy的共用接口,这样就在任何使用RealSubject的地方都可以使用Proxy。
RealSubject定义Proxy所代表的实体。
适用性
Proxy模式适用于在需要比较通用和复杂的对象指针代替简单的指针的时候
1、远程代理(Remote Proxy)为一个对象在不同地址空间提供局部代表。
2、虚代理(Virtual Proxy)根据需要创建开销很大的对象。
3、保护代理(Protection Proxy)控制对原始对象的访问,用于对象应该有不同的访问权限的时候。
4、智能引用(Smart Reference)取代了简单的指针,它在访问对象时执行一些附加操作。
代理模式代码
public class ProxyPattern {
public static void main(String[] args) {
RealSubject realSubject = new RealSubject();
Proxy proxy = new Proxy(realSubject);
proxy.buy();
}
}
interface Subject {
public void buy();
}
class Proxy implements Subject {
protected RealSubject realSubject;
public Proxy(RealSubject realSubject) {
this.realSubject = realSubject;
}
@Override
public void buy() {
System.out.println("办理购买前的手续");
realSubject.buy(); // 付钱
System.out.println("办理购买后的手续");
}
}
class RealSubject implements Subject {
@Override
public void buy() {
System.out.println("付钱");
}
}
行为型设计模式
行为模式涉及算法和对象间职责的分配。行为模式不仅描述对象或类的模式,还描述他们之间的通信方式。
行为类模式使用继承机制在类间分派行为。
行为对象模式使用对象复合而不是继承。一些行为对象模式描述了一组对等的对象怎么相互协作完成其中一个任务都无法单独完成的任务。
责任链模式(Chain of Responsibility)
意图
使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止。
结构
1、Handler定义一个处理请求的接口;(可选)实现后继链。
2、ConcreteHandler处理它所负责的请求;可访问它的后继者;如果可处理该请求,就处理它,否则将该请求转发给后继者。
3、Client向链上的具体处理者(ConcreteHandler)对象提交请求。
适用性
1、有多个的对象可以处理一个请求,哪个对象处理该请求运行时刻自动确实。
2、想在不明确指定接收者的情况下向多个对象中的一个提交一个请求。
3、可处理一个请求的对象集合应被动态指定。
责任链模式代码
public class ChainOfResponsibilityPattern {
public static void main(String[] args) {
Handler fudaoyuan = new FuDaoYuan();
Handler yuanzhang = new YuanZhang();
Handler xiaozhang = new XiaoZhang();
fudaoyuan.setNext(yuanzhang);
yuanzhang.setNext(xiaozhang);
fudaoyuan.HandlerRequest(31);
}
}
abstract class Handler {
protected Handler next;
public void setNext(Handler next) {
this.next = next;
}
public abstract void HandlerRequest(int request);
}
class FuDaoYuan extends Handler { // <= 7 审批
@Override
public void HandlerRequest(int request) {
if (request <= 7) {
System.out.println("辅导员审批通过");
} else {
if (next != null) {
next.HandlerRequest(request);
} else {
System.out.println("无法审批");
}
}
}
}
class YuanZhang extends Handler { // <= 15 审批
@Override
public void HandlerRequest(int request) {
if (request <= 15) {
System.out.println("院长审批通过");
} else {
if (next != null) {
next.HandlerRequest(request);
} else {
System.out.println("无法审批");
}
}
}
}
class XiaoZhang extends Handler { // <= 30 审批
@Override
public void HandlerRequest(int request) {
if (request <= 30) {
System.out.println("校长审批通过");
} else {
if (next != null) {
next.HandlerRequest(request);
} else {
System.out.println("无法审批");
}
}
}
}
命令模式(Command)
意图
将一个请求封装为一个对象,从而使得可以用不同的请求对客户进行参数化;对请求排队或记录请求日志,以及支持可撤销的操作。
结构
1、Command声明执行操作的接口。
2、ConcreteCommand将一个接收者对象绑定于一个动作;调用接收者相应的操作,以实现Execute。
3、Client创建一个具体命令对象并设定它的接收者。
4、Invoker要求该命令执行这个请求。
5、Receiver知道如何实施与执行一个请求相关操作。任何类都可能作为一个接收者。
适用性
1、抽象出待执行的动作以参数化某对象。
2、在不同的时刻指定、排列和执行请求。
3、支持取消操作。
4、支持修改日志。
5、(了解即可)用构建在原语操作上的高层操作构造一个系统。
命令模式代码
public class CommandPattern {
public static void main(String[] args) {
Tv tv = new Tv(); // 接收者 对象 电视机
Command onCommand = new OnCommand(tv); // 命令对象 开机命令
Command offCommand = new OffCommand(tv); // 命令对象 关机命令
Invoker invoker = new Invoker(); // 请求者
invoker.setCommand(onCommand); // 给请求者设置 开机 命令
invoker.call(); // 请求者去请求命令
System.out.println("========================================");
invoker.setCommand(offCommand); // 给请求者设置 关机命令
invoker.call(); // 请求者去请求命令
}
}
class Invoker { // 请求者
private Command command; // 命令
public void setCommand(Command command) { // 设置请求者 的 请求的命令
this.command = command;
}
public void call() { // 调用
command.Execute();
}
}
interface Command { // 命令接口
public void Execute(); // 执行命令
}
class OnCommand implements Command { // 开机命令
private Tv tv;
public OnCommand(Tv tv) {
this.tv = tv;
}
@Override
public void Execute() {
tv.OnAction();
}
}
class OffCommand implements Command { // 关机命令
private Tv tv;
public OffCommand(Tv tv) {
this.tv = tv;
}
@Override
public void Execute() {
tv.OffAction();
}
}
class Tv { // 接收者 电视机
public void OnAction() { // 开机行为
System.out.println("电视机开机了...");
}
public void OffAction() { // 关机行为
System.out.println("电视机关机了...");
}
}
解释器模式(Interpreter)
意图
给定一个语言,定义它的文法的一种表示,并定义一个解释器,这个解释器使用该表示来解释语言中的句子。
结构
1、AbstractExpression声明一个程序的解释操作,这个接口为抽象语法树中所有的节点所共享。
2、TerminalExpression实现与文法中的终结符相关联的解释操作;一个句子中的每个终结符需要该类的一个实例。
3、NonterminalExpression对文法中的每一条规则都需要一个NonterminalExpression类;为每个符号都维护一个AbstractExpression类型的实例变量;为文法中的非终结符实现解释操作。
4、Context包含解释器之外的一些全局信息。
5、Client构建(或被给定)表示该文法定义的语言中一个特定的句子的抽象语法树,该抽象语法树由NonterminalExpression和TerminalExpression的实例装配而成;调用解释操作。
适用性
当有一个语言需要解释器执行,并且可将语言中的句子表示为一个抽象语法树时。
1、该文法简单。
2、效率不是一个关键问题。
解释器模式代码
import java.util.*;
public class InterpreterPattern {
public static void main(String[] args) {
Context context = new Context();
context.check("A区的开发人员");
context.check("B区的调试人员");
context.check("C区的测试人员");
System.out.println("==========");
context.check("D区的程序员");
context.check("D区的测试员");
context.check("A区的程序员");
}
}
class Context {
private String[] regions = {"A区", "B区", "C区"};
private String[] persons = {"开发人员", "测试人员", "调试人员"};
private NonterminalExprssion nonterminal;
public Context() {
TerminalExpression region = new TerminalExpression(regions);
TerminalExpression person = new TerminalExpression(persons);
nonterminal = new NonterminalExprssion(region, person);
}
public void check(String info) {
boolean bool = nonterminal.Interpret(info);
if (bool) {
System.out.println("识别成功");
} else {
System.out.println("识别失败");
}
}
}
interface Expression {
public boolean Interpret(String info);
}
class NonterminalExprssion implements Expression {
private TerminalExpression region;
private TerminalExpression person;
public NonterminalExprssion(TerminalExpression region, TerminalExpression person) {
this.region = region;
this.person = person;
}
@Override
public boolean Interpret(String info) {
String[] str = info.split("的");
// B区的调试人员 --> str = {"B区", "调试人员"}
return region.Interpret(str[0]) && person.Interpret(str[1]);
}
}
class TerminalExpression implements Expression {
private Set<String> set = new HashSet<>();
public TerminalExpression(String[] data) {
// for (遍历对象类型 对象名 : 遍历对象)
for (String str : data) {
set.add(str);
}
}
@Override
public boolean Interpret(String info) {
return set.contains(info);
}
}
迭代器模式(Iterator)
意图
提供一种方法顺序访问一个聚合对象中的各个元素,且不需要暴露该对象的内部表示。
结构
1、Iterator(迭代器)定义访问和遍历元素的接口。
2、ConcreteIterator(具体迭代器)实现迭代器接口;对该聚合遍历时跟踪当前位置。
3、Aggregate(聚合)定义创建相应迭代器对象的接口。
4、ConcreteAggregate(具体聚合)实现创建相应迭代器的接口,该操作返回ConcreteIterator的一个适当的实例。
适用性
1、访问一个聚合对象的内容而无须暴露它的内部表示。
2、支持对聚合对象的多种遍历。
3、为遍历不同的聚合结构提供一个统一的接口。
迭代器代码
import java.util.*;
public class IteratorPattern {
public static void main(String[] args) {
BookAggregate bookAggregate = new BookAggregate();
String[] books = {"数据结构", "操作系统", "计算机网络", "计算机组成原理"};
double[] prices = {10.24, 20.48, 40.96, 81.92};
for (int i = 0; i < 4; i ++ ) {
bookAggregate.Add(new Book(books[i], prices[i]));
}
Iterator bookIterator = bookAggregate.CreateIterator();
while (bookIterator.hasNext()) {
Book book = (Book) bookIterator.next();
System.out.println(book.getName() + " " + book.getPrice());
}
}
}
interface Iterator {
public boolean hasNext();
public Object next();
}
class BookIterator implements Iterator {
private int index;
private BookAggregate bookAggregate;
public BookIterator(BookAggregate bookAggregate) {
this.index = 0;
this.bookAggregate = bookAggregate;
}
@Override
public boolean hasNext() {
if (index < bookAggregate.getSize()) {
return true;
} else {
return false;
}
}
@Override
public Object next() {
Object obj = bookAggregate.get(index);
index ++ ;
return obj;
}
}
interface Aggregate {
public Iterator CreateIterator();
}
class BookAggregate implements Aggregate {
private List<Book> list = new ArrayList<Book>();
public void Add(Book book) {
list.add(book);
}
public Book get(int index) {
return list.get(index);
}
public int getSize() {
return list.size();
}
@Override
public Iterator CreateIterator() {
return new BookIterator(this);
}
}
class Book {
private String name;
private double price;
public Book(String name, double price) {
this.name = name;
this.price = price;
}
public String getName() {
return name;
}
public double getPrice() {
return price;
}
}
中介者模式(Mediator)
意图
用一个中介对象来封装一系列的对象交互。中介者使各对象不需要显式地相互引用,从而使其耦合松散,而且可以对立地改变它们之间的交互。
结构
1、Mediator(中介者)定义一个接口用于各同事(Colleague)对象通信。
2、ConcreteMediator(具体中介者)通过协调各同事对象实现协作行为;了解并维护它的各个同事。
3、Colleague(同事类)知道它的中介者对象;每个同事类对象在需要与其他同事通信的时候与它的中介者通信。
适用性
1、一组对象定义良好但是复杂的方式进行通信,产生的相互依赖关系结构混乱且难以理解。
2、一个对象引用其他很多对象并且直接与这些对象通信,导致难以复用该对象。
3、想定制一个分布在多个类中的行为,而又不想生成太多的子类。
中介者模式代码
public class MediatorPattern {
public static void main(String[] args) {
ConcreteMediator mediator = new ConcreteMediator();
Colleague1 colleague1 = new Colleague1(mediator);
Colleague2 colleague2 = new Colleague2(mediator);
mediator.setColleague1(colleague1);
mediator.setColleague2(colleague2);
colleague1.sendMessage("软考加油");
colleague2.sendMessage("祝大家软考顺利通过!");
}
}
abstract class Colleague {
protected Mediator mediator;
}
class Colleague1 extends Colleague {
public Colleague1(Mediator mediator) {
this.mediator = mediator;
}
public void sendMessage(String message) {
mediator.sendMessage(message, this);
}
public void Notify(String message) {
System.out.println("同事1收到消息:" + message);
}
}
class Colleague2 extends Colleague {
public Colleague2(Mediator mediator) {
this.mediator = mediator;
}
public void sendMessage(String message) {
mediator.sendMessage(message, this);
}
public void Notify(String message) {
System.out.println("同事2收到消息:" + message);
}
}
abstract class Mediator {
public abstract void sendMessage(String message, Colleague colleague);
}
class ConcreteMediator extends Mediator {
private Colleague1 colleague1;
private Colleague2 colleague2;
public void setColleague1(Colleague1 colleague1) {
this.colleague1 = colleague1;
}
public void setColleague2(Colleague2 colleague2) {
this.colleague2 = colleague2;
}
public void sendMessage(String message, Colleague colleague) {
if (colleague == colleague1) {
colleague2.Notify(message); // 让同事2收到消息
} else {
colleague1.Notify(message); // 让同事1收到消息
}
}
}
备忘录模式(Memento)
意图
在不破坏封装的前提下捕获一个对象的内部状态,并在对象之外保存这个状态。这样以后就可以将对象恢复到原先保存的状态。
结构
1、Memento(备忘录)存储原发器对象的内部状态,原发器根据需要决定备忘录存储原发器的哪些内部状态;防止原发器以外的其他对象访问备忘录。
2、Qriginator(原发器)创建一个备忘录,用于记录当前时刻它的内部状态;使用备忘录恢复内部状态。
3、Caretaker(管理者)负责保存好备忘录;不能对备忘录的内容进行操作或检查。
适用性
1、必须保存一个对象在某一时刻的(部分)状态,这样以后需要时它才能恢复到先前的状态。
2、如果一个用接口来让其他对象直接得到这些状态,将会暴露对象的实现细节并破坏对象的封装性。
备忘录模式代码
import java.util.*;
public class MementoPattern {
public static void main(String[] args) {
Caretaker caretaker = new Caretaker();
Originator originator = new Originator();
originator.setState("1024");
Memento backup1 = originator.createMemento();
caretaker.addMemento(backup1);
originator.setState("2048");
Memento backup2 = originator.createMemento();
caretaker.addMemento(backup2);
originator.setState("4096");
Memento backup3 = originator.createMemento();
caretaker.addMemento(backup3);
System.out.println(originator.getState());
caretaker.showMemento();
Memento memento1 = caretaker.getMemento(2);
originator.setMemento(memento1);
System.out.println("根据第2次备份还原之后的状态为:" + originator.getState());
}
}
class Originator { // 原发器
private String state;
public void setState(String state) {
this.state = state;
}
public String getState() {
return state;
}
public Memento createMemento() {
return new Memento(state);
}
public void setMemento(Memento memento) {
state = memento.getState();
}
}
class Memento { // 备忘录
private String state;
public Memento(String state) {
this.state = state;
}
public String getState() {
return state;
}
}
class Caretaker { // 管理者
private List<Memento> mementoList = new ArrayList<>();
public void addMemento(Memento memento) {
mementoList.add(memento);
}
public Memento getMemento(int index) {
// 判断参数是否合法
if (index >= 1 && index <= mementoList.size()) {
return mementoList.get(index - 1);
}
return null;
}
public void showMemento() {
int cnt = 1;
// for (遍历对象类型 对象名 : 遍历对象)
for (Memento memento : mementoList) {
System.out.println("第" + cnt + "次备份,状态为:" + memento.getState());
cnt ++ ;
}
}
}
观察者模式(Observer)
意图
定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。
结构
1、Subject(目标)知道它的观察者,可以有任意多个观察者观察同一个目标;提供注册和删除观察者对象的接口。
2、Observer(观察者)那些在目标发生改变时需获得通知的对象定义一个更新接口。
3、ConcreteSubject(具体目标)将有关状态存入各ConcreteObserver对象;当它的状态发生改变时,向它的各个观察者发出通知。
4、ConcreteObserver(具体观察者)维护一个指向ConcreteSubject对象的引用;存储有关状态,这些状态与目标的状态保持一致;实现Observer的更新接口,以使自身状态与目标状态保持一致。
适用性
1、(了解即可)当一个抽象模型有两个方面,其中一个方面依赖于另一个方面,将这两者封装在独立的对象中以使它们可以各自地改变和复用。
2、当对一个对象的改变需要同时改变其他对象,而不知道具体多少对象有待改变时。
3、当一个对象必须通知其他对象,而它又不能假定其他对象是谁,即不希望这些对象是紧耦合的。
观察者模式代码
import java.util.*;
public class ObserverPattern {
public static void main(String[] args) {
Subject subjectA = new ConcreteSubject("目标A");
Observer observerB = new ConcreteObserver("张三", subjectA);
Observer observerC = new ConcreteObserver("李四", subjectA);
Observer observerD = new ConcreteObserver("王五", subjectA);
subjectA.setState("更新了");
System.out.println("======================================");
subjectA.Detach(observerD);
subjectA.setState("停更了");
}
}
interface Subject { // 目标
public void Attach(Observer observer); // 添加观察者
public void Detach(Observer observer); // 删除观察者
public void Notify(); // 状态改变后 通知所有观察者
public void setState(String state); // 设置状态(改变状态)
public String getState(); // 获取状态
}
class ConcreteSubject implements Subject {
private String name;
private String state;
private List<Observer> observerList;
public ConcreteSubject(String name) {
state = "未更新";
this.name = name;
observerList = new ArrayList<Observer>();
}
public void setState(String state) {
this.state = state;
System.out.println(name + "的状态发生变化,变化后的状态为:" + state);
Notify();
}
public String getState() {
return state;
}
public void Attach(Observer observer) {
observerList.add(observer);
}
public void Detach(Observer observer) {
observerList.remove(observer);
}
public void Notify() {
// for (遍历对象类型 对象名 : 遍历对象)
for (Observer observer : observerList) {
observer.update();
}
}
}
interface Observer { // 观察者接口
public void update(); // 收到通知 更新观察者的状态
}
class ConcreteObserver implements Observer {
private String name;
private String state;
private Subject subject;
public ConcreteObserver(String name, Subject subject) {
this.name = name;
this.subject = subject;
subject.Attach(this);
state = subject.getState();
}
@Override
public void update() {
System.out.println(name + "收到通知");
state = subject.getState(); // 让当前观察者的状态 和 改变了状态之后的目标的状态保持一致
System.out.println(name + "改变后的状态为:" + state);
}
}
状态模式(State)
意图
允许一个对象在其内部状态改变时改变它的行为。对象看起来似乎修改了它的类。
结构
1、Context(上下文)定义客户感兴趣的接口;维护一个ConcreteState子类的实例,这个实例定义当前状态。
2、State(状态)定义一个接口以封装与Context的一个特定状态相关的行为。
3、ConcreteState(具体状态子类)每个子类实现与Context的一个状态相关的行为。
适用性
1、一个对象的行为决定于它的状态,并且它必须在运行时刻根据状态改变它的行为。
2、一个操作中含有庞大的多分支的条件语句,且这些分支依赖于该对象的状态。这个状态常用一个或多个枚举常量表示。
状态模式代码
public class StatePattern {
public static void main(String[] args) {
Context context = new Context(); // count:3
System.out.println(context.getState());
context.Request(); // 购买一个饮料 count = 2
context.Request(); // 购买一个饮料 count = 1
context.Request(); // 购买一个饮料 count = 0
System.out.println(context.getState());
context.Request(); // 无货 等待补货 补货成功 count = 5
System.out.println(context.getState());
context.Request(); // 购买一个饮料 count = 4
System.out.println(context.getCount());
}
}
class Context { // 贩卖机
private int count;
private State state;
public Context() {
count = 3;
state = new StateA();
}
public int getCount() {
return count;
}
public void setCount(int count) {
this.count = count;
}
public State getState() {
return state;
}
public void setState(State state) {
this.state = state;
}
public void Request() { // 购买一个饮料
state.Handle(this);
}
}
interface State {
public void Handle(Context context);
}
class StateA implements State { // 有货
@Override
public void Handle(Context context) {
int count = context.getCount();
if (count >= 1) {
System.out.println("购买成功!");
context.setCount(count - 1);
if (context.getCount() == 0) {
context.setState(new StateB());
}
} else {
System.out.println("购买失败!");
}
}
}
class StateB implements State { // 无货
@Override
public void Handle(Context context) {
int count = context.getCount();
if (count == 0) {
System.out.println("购买失败!等待补货");
context.setCount(5);
System.out.println("补货成功,请重新购买");
context.setState(new StateA());
}
}
}
策略模式(Strategy)
意图
定义一系列的算法,把它们一个一个封装起来,并且使它们可以互相替换。此模式使得算法可以独立于使用它们的客户而变化。
结构
1、Strategy(策略)定义所有支持的算法的公共接口。Context使用这个接口来调用某ConcreteStrategy定义的算法。
2、ConcreteStrategy(具体策略)以Strategy接口实现某具体算法。
3、Context(上下文)用一个ConcreteStrategy对象来配置;维护一个对Strategy对象的引用;可定义一个接口来让Strategy访问它的数据。
适用性
1、许多相关的类仅仅是行为有异。“策略”提供一种用多个行为中的一个行为来配置一个类的方法。
2、需要使用一个算法的不同变体。
3、算法使用客户不应该知道的数据。可使用策略模式以避免暴露复杂的、与算法相关的数据结构。
4、一个类定义了多种行为,并且这些行为在这个类的操作中以多个条件语句的形式出现,将相关的条件分支移入它们各自的Strategy类中,以替代这些条件语句。
策略模式代码
public class StrategyPattern {
public static void main(String[] args) {
Strategy add = new AddStrategy();
Strategy subtraction = new SubtractionStrategy();
Strategy multiply = new MultiplyStrategy();
OperationContext context = new OperationContext(add);
context.Operation(2022, 528);
context = new OperationContext(subtraction);
context.Operation(2022, 528);
context = new OperationContext(multiply);
context.Operation(2022, 528);
}
}
class OperationContext {
private Strategy strategy;
public OperationContext(Strategy strategy) {
this.strategy = strategy;
}
public void Operation(int a, int b) {
strategy.TwoNumberOperation(a, b);
}
}
interface Strategy {
public void TwoNumberOperation(int a, int b);
}
class AddStrategy implements Strategy {
@Override
public void TwoNumberOperation(int a, int b) {
System.out.println(a + b);
}
}
class SubtractionStrategy implements Strategy {
@Override
public void TwoNumberOperation(int a, int b) {
System.out.println(a - b);
}
}
class MultiplyStrategy implements Strategy {
@Override
public void TwoNumberOperation(int a, int b) {
System.out.println(a * b);
}
}
模板方法模式(Template Method)
意图
定义一个操作中的算法骨架,而将一些步骤延迟到子类中。Template Method使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。
结构
1、AbstractClass(抽象类)定义抽象的原语操作,具体的子类将重定义它们以实现一个算法的各步骤;实现模板方法,定一个算法的骨架,该模板方法不仅调用原语操作,也调用定义在AbstractClass或其他对象中的操作。
2、ConcreteClass(具体类)实现原语操作以完成算法中与特定子类相关的步骤。
适用性
1、一次性实现一个算法的不变的部分,并将可变的行为留给子类来实现。
2、各子类中公共的行为应被提取出来并集中到一个公共父类中,以避免代码重复。
3、(了解即可)控制子类扩展。模板方法旨在特定点调用“hook”操作(默认的行为,子类可以在必要时进行重定义扩展),这就只允许在这些点进行扩展。
模板方法模式代码
public class TemplateMethodPattern {
public static void main(String[] args) {
// 父类名 对象名 = new 子类名();
Person student = new Student();
Person teacher = new Teacher();
student.TemplateMethod();
System.out.println("=====我是分割线=====");
teacher.TemplateMethod();
}
}
abstract class Person {
public void TemplateMethod() {
System.out.println("上课 去教室"); // 1
PrimitiveOperation1(); // 2
System.out.println("下课 离开教室"); // 3
PrimitiveOperation2(); // 4
}
public abstract void PrimitiveOperation1(); // 原语操作 1 :上课过程 学生 听课…… 老师 讲课
public abstract void PrimitiveOperation2(); // 原语操作 2 :作业 学生 写作业 提交作业…… 老师 批改作业 打分数
}
class Student extends Person {
@Override
public void PrimitiveOperation1() {
System.out.println("学生:听课 学习 做笔记 提出问题");
}
@Override
public void PrimitiveOperation2() {
System.out.println("学生:写作业 提交作业");
}
}
class Teacher extends Person {
@Override
public void PrimitiveOperation1() {
System.out.println("老师:上课 讲课 解答问题 布置作业");
}
@Override
public void PrimitiveOperation2() {
System.out.println("老师:批改作业 打分数");
}
}
访问者模式(Visitor)
意图
表示一个作用于某对象结构中的各元素的操作。它允许在不改变各元素的类的前提下定义作用于这些元素的新操作。
结构
1、Visitor(访问者)为该对象结构中ConcreteElement的每一个类声明一个Visit操作。该操作的名字和特征标识了发送Visit请求给该访问者的那个类,这使得访问者可以确定正被访问元素的具体的类。这样访问者就可以通过该元素的特定接口直接访问它。
2、ConcreteVisitor(具体访问者)实现每个有Visitor声明的操作,每个操作实现本算法的一部分,而该算法片段乃是对应结构中对象的类。ConcreteVisitor为该算法提供了上下文并存储它的局部状态。这一状态常常在遍历该结构的过程中累积结果。
3、Element(元素)定义以一个访问者为参数的Accept操作。
4、ConcreteElement(具体元素)实现以一个访问者为参数的Accept操作。
5、ObjectStructure(对象结构)能枚举它的元素;可以提供一个高层的接口以允许该访问者访问它的元素;可以是一个组合或者一个集合,如一个列表或一个无序集合。
适用性
1、一个对象结构包含很多类对象,它们有不同的接口,而用户想对这些对象实施一些依赖于其具体类的操作。
2、(了解即可)需要对一个对象结构中的对象进行很多不同的并且不相关的操作,而又想避免这些操作“污染”这些对象的类。
3、(了解即可)定义对象结构的类很少改变,但经常需要再此结构上定义新的操作。
访问者模式代码
import java.util.*;
public class VisitorPattern {
public static void main(String[] args) {
PersonStructure structure = new PersonStructure();
Visitor1 visitor1 = new Visitor1();
System.out.println("访问者1的访问记录:");
structure.Accept(visitor1);
System.out.println("学生年龄的总和:" + visitor1.getStudentAgeSum() + " 老师年龄的总和:" + visitor1.getTeacherAgeSum());
System.out.println("=========================================");
Visitor2 visitor2 = new Visitor2();
System.out.println("访问者2的访问记录:");
structure.Accept(visitor2);
System.out.println("学生的最高成绩:" + visitor2.getMaxScore() + " 老师的最高工龄:" + visitor2.getMaxWorkYear());
}
}
interface Visitor {
public void visitStudent(Student student); // 访问学生
public void visitTeacher(Teacher teacher); // 访问老师
}
class Visitor1 implements Visitor { // 访问者1 分别统计学生和老师的年龄总和
private int studentAgeSum = 0;
private int teacherAgeSum = 0;
public int getStudentAgeSum() {
return studentAgeSum;
}
public int getTeacherAgeSum() {
return teacherAgeSum;
}
@Override
public void visitStudent(Student student) {
System.out.println("访问者1访问学生:" + student.getName() + " 年龄:" + student.getAge());
studentAgeSum += student.getAge();
}
@Override
public void visitTeacher(Teacher teacher) {
System.out.println("访问者1访问老师:" + teacher.getName() + " 年龄:" + teacher.getAge());
teacherAgeSum += teacher.getAge();
}
}
class Visitor2 implements Visitor { // 访问者2 分别求出 学生的最高成绩 以及 老师的最高工龄
private int maxScore = -1;
private int maxWorkYear = -1;
public int getMaxScore() {
return maxScore;
}
public int getMaxWorkYear() {
return maxWorkYear;
}
@Override
public void visitStudent(Student student) {
System.out.println("访问者2访问学生:" + student.getName() + " 成绩:" + student.getScore());
maxScore = Math.max(maxScore, student.getScore());
}
@Override
public void visitTeacher(Teacher teacher) {
System.out.println("访问者2访问老师:" + teacher.getName() + " 工龄:" + teacher.getWorkYear());
maxWorkYear = Math.max(maxWorkYear, teacher.getWorkYear());
}
}
class PersonStructure {
private List<Person> personList = new ArrayList<Person>();
public PersonStructure() {
personList.add(new Student("张三", 20, 70));
personList.add(new Student("李四", 21, 80));
personList.add(new Student("王五", 22, 90));
personList.add(new Teacher("李老师", 26, 3));
personList.add(new Teacher("陈老师", 27, 4));
personList.add(new Teacher("刘老师", 28, 5));
}
public void Accept(Visitor visitor) {
// for (遍历对象类型 对象名 : 遍历对象)
for (Person person : personList) {
person.Accept(visitor);
}
}
}
abstract class Person {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
public abstract void Accept(Visitor visitor);
}
class Student extends Person {
private int score;
public Student(String name, int age, int score) {
super(name, age);
this.score = score;
}
public int getScore() {
return score;
}
@Override
public void Accept(Visitor visitor) {
visitor.visitStudent(this);
}
}
class Teacher extends Person {
private int workYear;
public Teacher(String name, int age, int workYear) {
super(name, age);
this.workYear = workYear;
}
public int getWorkYear() {
return workYear;
}
@Override
public void Accept(Visitor visitor) {
visitor.visitTeacher(this);
}
}
八、操作系统
操作系统地位
进程管理
死锁
m为资源数量,n为进程数量,k为每个进程需要的资源数量
m < n*k时,即资源数小于进程所要求的总数时,可能会引起死锁
只要满足 m >= n*(k-1)+1 那就不会发生死锁
存储管理
分页式存储管理
分页地址结构
4k表示页内地址有12位,C20就是页内地址,所以页号为1(不用转,直接是多少那就页号是多少),对应物理块号为3,那物理地址就是3C20H
段页式存储管理
段号是31-24+1=8,页号是23-12+1=12,页内地址是11-0+1=12,也就分别是
2^8,2^12,2^12
缓冲区
单缓冲区
T为输入时间,M为传输时间,n为作业个数,C为处理时间
计算单缓冲区花费的时间:(T+M)*n+C
双缓冲区
T为输入时间,M为传输时间,n为作业个数,C为处理时间
计算双缓冲区花费的时间:T*n+M+C
移臂调度算法
先来先服务(FCFS)
按请求访问者的先后次序启动磁盘驱动器,而不考虑它们要访问的物理位置
最短寻道时间优先(SSTF)
让离当前磁道最近的请求访问者启动磁盘驱动器,即让查找时间最短的那个作业先执行
扫描算法或电梯调度算法(SCAN)
总是从磁头当前位置开始,沿磁头的移动方向去选择离当前最近的那个柱面的请求。如果沿磁头的方向无请求方向,就改变磁头移动方向
循环扫描算法或单向扫描算法(CSCAN)
先处理延磁头方向的,当该方向无请求,磁头调转方向回到反方向的最里端
设备管理
文件管理
多级索引结构
位示图
位示图:用二进制的一位来表示一个物理块的使用情况
位示图的大小由磁盘空间的大小(物理块总数)决定
4096/32 = 129 ; 200*1024/1/32 = 6400
九、结构化开发
耦合
耦合程度取决于:各个模块之间接口的复杂程度、调用模块的方式、通过接口的信息类型
无直接耦合
没有直接的关系
数据耦合
传递的是简单的数据值
标记耦合
传递的是数据结构
控制耦合
控制变量
外部耦合
软件之外的环境
公共耦合
公共数据环境
内容耦合
一个模块直接使用另一个模块的内部数据,或通过非正常入口,转入另一个模块内部
内聚
偶然内聚(巧合内聚)
没有任何联系
逻辑内聚
逻辑上相似的功能
时间内聚
同时执行的动作组合在一起
过程内聚
按指定的过程执行
通信内聚
在同一个数据结构上操作
顺序内聚
顺序执行
功能内聚
最强内聚,模块内所有元素共同作用完成一个功能
结构化分析
需求分析阶段输出:数据流图、实体联系图、状态迁移图、数据字典
模型核心:数据字典
描述软件使用和产生的所有数据对象。
为数据流图中的每个数据流、文件、加工,以及组成数据流或文件的数据项做出说明。
有4类条目:数据流、数据项、数据存储、基本加工。
数据模型:E-R图
描述数据对象间关系
功能模型:DFD(数据流图)表达
描述了系统分解,但没有对图中各成分进行说明。
行为模式:UML(状态转换图)
描绘系统状态和在不同状态间转换方式。
十、软件工程
能力模型
CMM(能力成熟度模型)
1)初始级
软件过程的特点是杂乱无章,有时甚至很混乱,几乎没有明确定义的步骤
2)可重复级
建立了基本的项目管理过程和实践来跟踪项目费用
3)已定义级
管理和工程两方面的软件过程已经文档化、标准化
4)已管理级
制定了软件过程和产品质量的详情度量标准
5)优化级
加强了定量分析,通过来自过程质量反馈和来自新观念、新技术的反馈使过程能不断持续地改进
CMMII(能力成熟度集成模型)
CL0(未完成的):过程或未执行或未得到CL1中定义的所有目标
CL1(已执行的):将可标识的输入工作产品转换成可标识的输出
CL2(已管理的):集中于已管理的过程的制度化
CL3(已定义级的):已定义的过程的制度化
CL4(定量管理的):可定量管理的过程的制度化
CL5(优化的):量化(统计学)手段改变和优化过程域
开发模型
瀑布模型(需求明确)
瀑布模式适合开发需求明确的,需求大致固定不会随意变更的系统
V模式的关键字在于质量保证活动和沟通,基本问题逐步细化
增量模型(快速构建)
增量模型拥有瀑布模型的所有优点,它主要的特点是可以快速构造可运行的产品
优点:
第一个可交付版本所需要的成本和时间很少;
开发由增量表示小系统所承担风险不大;
减少用户需求的变更;
可以仅对一个或两个增量投资
缺点:
管理发生的成本、进度和配置的复杂性可能会超出组织的能力
演化(迭代)模型
演化模型是迭代的过程模型,能够逐步开发出更完整的软件版本
演化模型适用于对软件需求缺乏准确认识的情况
典型的演化模型有原型模型和螺旋模型等
原型模型(需求模糊,规模小)
适合需求模糊不清且系统规模不大
螺旋模型(风险分析,规模大,需求变化)
螺旋模型的特点是加入了风险分析,适合大规模高风险的,需求变化的系统
需要开发人员具有相当丰富的风险评估经验和专门知识
过多的迭代次数会增加开发成本,延迟提交时间
喷泉模型(面向对象)
以用户需求为动力,适合于面向对象的开发方法
喷泉模型使开发过程具有:迭代性和无间隙性
不存在明显边界
允许各开发活动交叉、迭代地进行
优点:可以提高软件项目的开发效率,节省开发时间
缺点:需要大量的开发人员,不利于项目的管理
统一过程(UP)模型
用例和风险驱动,以架构为中心,迭代且增量
1)起始阶段
产生的主要工作产品有构想文档
2)精化阶段
进行需求分析和架构演进
3)构建阶段
构建阶段关注系统的构建,产生实现模型
4)移交阶段
产生软件增量
初始阶段:生命周期目标
精化阶段:生命周期架构
构建阶段:初始运作功能
移交阶段:产品发布
敏捷开发
尽可能早地、持续地对有价值的软件交付
1.极限编程(XP)
XP是一种轻量级(敏捷)、高效、低风险、柔性、可预测的、科学的软件开发方式
4大价值观:沟通、简单性、反馈和勇气
5个原则:快速反馈、简单性假设、逐步修改、提倡更改和优质工作
2.水晶法(Crystal)
水晶法认为每个不同的项目都需要一套不同的策略、约定和方法论
3.并列争求法(Scrum)
并列争求法使用迭代的方法,把每30天一次的迭代称为一个“冲刺”
4.自适应软件开发(ASD)
5.敏捷统一过程(AUP)
在大型上连续,在小型上迭代
建模
实现
测试
部署
配置及项目管理
环境管理
开发过程
需求分析
概要设计(模块、接口、数据结构、数据库)
1)设计软件系统总体结构
其基本任务是采用某种设计方法,将一个复杂的系统按功能划分成模块;确定每个模块的功能;确定模块之间的调用关系;确定模块之间的接口,即模块之间传递的信息;评价模块结构的质量。
2)数据结构及数据库设计
(1)数据结构设计
(2)数据库设计
①概念设计
②逻辑设计
③物理设计
3)编写概要设计文档
文档主要有概要设计说明书、数据库设计说明书、用户手册以及修订测试计划。
4)评审
详细设计(数据结构、算法、数据库)
1)对每个模块进行详细的算法设计,用某种图形、表格和语言等工具将每个模块处理过程的详细算法描述出来。
2)对模块内的数据结构进行设计。
3)对数据库进行物理设计,即确定数据库的物理结构。
4)其他设计。根据软件系统的类型,还可能要进行以下设计。
①代码设计。为了提高数据的输入、分类、存储和检索等操作,节约内存空间,对数据库中某些数据项的值要进行代码设计。
②输入/输出格式设计。
③用户界面设计。
5)编写详细设计说明书。
6)评审。对处理过程的算法和数据库的物理结构都要评审。
系统设计的结果是一系列的系统设计文件,这些文件是物理实现一个信息系统(包括硬件设备和编制软件程序)的重要基础。
测试
系统测试
意义:成功的测试是发现了至今尚未发现的错误
目的:以最少的人力和时间发现潜在的各种错误和缺陷
单元测式
单元测试侧重于模块中的内部处理逻辑和数据结构
如果选用机器测试,一般用白盒测试法
集成测试
自顶向下集成,不需要驱动模块
自底向上集成,不需要桩模块
测试方法
软件测试方法分为静态测试和动态测试
黑盒测试(功能测试)
McCabe度量法
1、闭合区域 + 1
2、边 - 节点 + 2
白盒测试
白盒测试常用的技术是逻辑覆盖、循环覆盖和基本路径测试
逻辑覆盖
①语句覆盖
每条语句至少执行一次
②判定覆盖(分支覆盖)
每条语句的真/假值至少通过一次
③条件覆盖
条件覆盖是指构造一组测试用例
条件覆盖就是判定表达式的所有条件都要最少取得一真一假,例如上面那个图的第一个判定表达式的条件如下,有两个条件,每个条件要最少取一真一假
④判定/条件覆盖
其实就是要同时满足判定覆盖和条件覆盖反正记得判定覆盖就是判定表达式最少一真一假,条件覆盖就是判定表达式的所有条件最少一真一假
⑤条件组合覆盖
满足条件组合覆盖的测试用例是一定满足判定覆盖、条件覆盖和判定/条件覆盖的。
也就是判定表达式的所有条件的真假不同组合必须都有,两个条件就是四种组合,例如下面的
⑥路径覆盖
路径覆盖是指覆盖被测试程序中所有可能的路径
循环覆盖
基本路径测试
调试
调试方法
试探法
回溯法
对分查找法
归纳法
演绎法
维护
系统可维护性的评价指标
可理解性
可测试性
可修改性
软件维护
正确性维护
是指正在系统开发阶段已发生而系统测试阶段尚未发生的错误
适应性维护
是指使应用软件适应信息技术变化和管理需求变化而进行的修改
完善性维护
为了扩充功能和改善性能而进行的修改
预防性维护
为了改进应用软件的可靠性和维护性
可靠性、可用性、可维护性
可靠性
无效运作的概率
公式:MTTF/(1+MTTF)
可用性
正确运作的概率
公式:MTBF/(1+MTBF)
可维护性
维护活动的概率
公式:1/(1+MTTR)
COCOMO估算模型
COCOMO估算模型
1)基本COCOMO模型
静态单变量模型
2)中级COCOMO模型
静态多变量模型
3)详细COCOMO模型
系统、子系统和模块
COCOMOⅡ模型
1)应用组装模型
2)早期设计阶段模型
3)体系结构阶段模型
3种不同的规模估算选择:对象点、功能点、代码行
应用组装模型使用:对象点
早期设计阶段模型使用:功能点
功能点可以转换为代码行
软件配置管理
配置数据库分为三类:
开发库、受控库、产品库
ISO IEC 9126软件质量模型
十一、信息安全
病毒
计算机病毒的特征包括:传播性、隐蔽性、感染性、潜伏性、触发性、破坏性等
worm表示蠕虫病毒、Trojan表示特洛伊木马、Backdoor表示后门病毒、Macro表示宏病毒
宏病毒感染的对象主要是文本文档、电子表格等
木马软件:冰河
蠕虫病毒: 欢乐时光、熊猫烧香、红色代码、爱虫病毒,震网
特洛伊木马:通过服务器与客户端建立连接
网络攻击
1、拒绝服务攻击 (Dos攻击)
目的是使计算机或网络无法提供正常的服务
拒绝服务攻击是不断向计算机发起请求来实现的
2、重放攻击
攻击者发送一个目的主机已经接受过的报文来达到攻击目的
攻击者利用网络监听或者其他方式盗取认证凭据,之后再重新发送给认证服务器。
主要用于身份认证过程,目的是破坏认证的正确性。
3、口令入侵攻击
使用某些合法用户的账号和口令登录到目的主机,然后再实施攻击活动
4、特洛伊木马
被伪装成程序或游戏,当用户下载了带有木马的软件或附件时,这个程序就会向黑客发起连接请求,建立连接后黑客就实施攻击活动。
5、端口欺骗攻击
采用端口扫描找到系统漏洞从而实施攻击
6、网络监听
攻击者可以接收某一网段在同一条物理通道上传输的所有信息,使用网络监听可以轻松截取包括账号和口令在内的信息资料
7、IP欺骗攻击
产生的IP数据包为伪造的源IP地址,以便冒充其他系统或发件人的身份。
8、Sql注入攻击
是黑客对数据库进行攻击的常用手段之一。
没有对用户输入数据的合法性进行判断,使应用程序存在安全隐患。
攻击者可以提交一段数据库查询代码,根据程序返回的结果,获得某些他想得知的数据首先获取数据库的权限,就可获取用户账号和口令信息,以及对某些数据修改等。
9、入侵检测技术
专家系统、模型检测、简单匹配
网络安全
SSH协议在终端设备与远程站点之间建立安全连接
十二、计算机网络
网络设备
物理层
中继器
它是在物理层上实现局域网网段互连的,用于扩展局域网网段的长度。
集线器(多端口中继器)
集线器可以看成是一种特殊的多路中继器,也具有信号放大功能。
数据链路层
网桥
网桥用于连接两个局域网网段,工作于数据链路层。
交换机(多端口的网桥)
网络层
路由器
协议簇
FTP服务器:数据端口20;控制端口21;
十三、数据结构
复杂度
时间复杂度
算法时间复杂度以算法中基本操作重复执行的次数(简称为频度)作为算法的时间量。一般不必要精确计算出算法的时间复杂度,只要大致计算出相应的数量级即可
常见的对算法执行所需时间的度量:
常见算法逻辑的时间复杂度:
(1)单个语句,或程序无循环和复杂函数调用:O(1)
(2)单层循环:O(n);双层嵌套循环:O(n2);三层嵌套循环:O(n3)
(3)树形结构、二分法、构建堆过程:O(log2n)
(4)堆排序、归并排序:O(nlog2n)
(5)所有不同可能的排列组合:O(2n)
空间复杂度
定义的数据占用多少空间就是空间复杂度
渐进符号
渐进上界
大于等于平均时间复杂度
渐进下界
小于等于平均时间复杂度
渐进紧致界
等于平均时间复杂度
线性结构
存储结构
线性表的存储结构
相当于一个数组,所以可以直接通过下边快速查询到表中的元素,所以效率高,但是插入和删除会批量移动,所以效率低,简称查询高效率,插删低效率
顺序存储
插入元素时间复杂度
最好的情况:直接在顺序表后面插入一个元素,时间复杂度为O(1)
最坏的情况:在插入一个元素到原来第一个元素的位置,时间复杂度为O(n)
平均复杂度为O(n)
删除元素时间复杂度
最好的情况:直接在删除最后一个元素,时间复杂度为O(1)
最坏的情况:删除第一个元素,时间复杂度为O(n)
平均复杂度为O(n)
查询元素时间复杂度
时间复杂度为O(1),因为这是直接根据数组下边就可以快速查询到对应的元素
链式存储
线性表的链式存储是用通过指针链接起来的结点来存储数据元素
存储各数据元素的结点的地址并不要求是连续的
插入元素的时间复杂度
带头结点时间复杂度:
最好的情况:k=1的时候,也就是插入在头结点后面一个结点的位置时,时间复杂度为O(1)
最坏的情况:k=n+1的时候,也就是插入在最后面,时间复杂度为O (n)
平均复杂度为O (n)
不带头结点时间复杂度:
最好的情况:k=1的时候,也就是插入在第一个结点后面一个结点的位置时,时间复杂度为O (1)
最坏的情况:k=n+1的时候,也就是插入在最后面,时间复杂度为O (n)
平均复杂度为O (n)
删除元素时间复杂度
最好的情况:k=1的时候,也就是删除在头结点后面一个结点时,时间复杂度为O (1)
最坏的情况:k=n+1的时候,也就是删除最后面一个结点时,时间复杂度为O (n)
平均复杂度为O (n)
带不带头结点它们删除和插入的时间复杂度都是一样的
查找元素时间复杂度
最好的情况:k=1的时候,也就是查找在头结点后面一个结点时,时间复杂度为O (1)
最坏的情况:k=n+1的时候,也就是查找最后面一个结点时,时间复杂度为O (n)
平均复杂度为O (n)
二叉树
二叉树的性质
存储结构
1.顺序存储
2.链式存储
对于二叉链表来说,n个结点就有n+1个空指针域
对于三叉链表来说,n个结点就有n+2个空指针域
二叉树遍历
先序遍历:根左右
中序遍历:左根右
后序遍历:左右根
层次遍历:从上到下,从左往右
满二叉树
深度为k的二叉树有2^k-1个节点
完全二叉树
高度为h的完全二叉树中,除了第h层(最后一层),其余各层都是满的。在第h层上的结点必须从左到右一次放置,不能留空。
平衡二叉树
二叉树中任意一个结点的左右子树高度之差的绝对值不超过1
最优二叉树(哈夫曼树)
带权路径长度最短的树。
图
图的遍历
深度优先搜索(DFS)
邻接矩阵:时间复杂度O(n^2)
邻接表:时间复杂度O(n+e)
广度优先搜索(BFS)
邻接矩阵:时间复杂度O(n^2)
邻接表:时间复杂度O(n+e)
排序
插入排序(稳定 不归位)
直接插入排序
适用于序列基本上有序
- 将一个待排序的数组分成两部分,前一部分代表是有序序列,后一部分代表未排序序列
- 将第一个元素看做一个有序序列,把第二个元素到最后一个元素当成是未排序序列
- 从头到尾依次扫描未排序序列,将扫描到的每个元素插入有序序列的适当位置
- 如果待插入的元素与有序序列中的某个元素相等,则将待插入元素插入到相等元素的后面,直到未排序序列全部扫描完毕为止。
排序算法 | 平均时间复杂度 | 最坏时间复杂度 | 最好时间复杂度 | 空间复杂度 | 稳定性 |
---|---|---|---|---|---|
直接插入排序 | O(n^2) | O(n^2) | O(n) | O(1) | 稳定 |
希尔排序(不稳定)
缩小增量排序
- 按下标的一定增量进行分组,对每组使用插入排序算法排序
- 随着增量逐渐减少,每组包含的元素越来越多,当增量减至 1 时,恰被分成一组,算法便终止
排序算法 | 平均时间复杂度 | 最坏时间复杂度 | 最好时间复杂度 | 空间复杂度 | 稳定性 |
---|---|---|---|---|---|
希尔排序 | O(n^1.3) | O(n^2) | O(n) | O(1) | 不稳定 |
选择排序(不稳定 归位)
- 从待排序的元素中选出最小的元素,存放在起始位置,固定住该最小元素
- 同理取出未固定的元素中的最小元素,存放在起始位置,固定
- 以此类推,直到全部待排序的数据元素的个数为零。
排序算法 | 平均时间复杂度 | 最坏时间复杂度 | 最好时间复杂度 | 空间复杂度 | 稳定性 |
---|---|---|---|---|---|
简单选择排序 | O(n^2) | O(n^2) | O(n^2) | O(1) | 不稳定 |
堆排序(不稳定 归位)
- 将无序序列构建成一个堆,根据升序降序需求选择大顶堆或小顶堆
- 找出最大值放在堆顶位置后将堆顶元素依次固定到数组末端(这里用大顶堆得到升序序列)
- 重新调整结构,使其满足堆定义,然后继续交换堆顶元素与当前末尾元素,反复执行调整+交换步骤,直到整个序列有序
排序算法 | 平均时间复杂度 | 最坏时间复杂度 | 最好时间复杂度 | 空间复杂度 | 稳定性 |
---|---|---|---|---|---|
堆排序 | O(nlog2^n) | O(nlog2^n) | O(nlog2^n) | O(1) | 不稳定 |
冒泡排序(稳定 归位)
- 比较相邻的元素,如果第一个比第二个大,就交换他们两个
- 对每一对相邻元素做同样的工作,从开始的第一对到结尾的最后一对,这步做完后,最后的元素应该会是最大的数
- 每次过后,需要排序的元素就越来越少,对剩下需要排序的元素重复上面的步骤,直到没有任何一对数字需要比较
排序算法 | 平均时间复杂度 | 最坏时间复杂度 | 最好时间复杂度 | 空间复杂度 | 稳定性 |
---|---|---|---|---|---|
冒泡排序 | O(n^2) | O(n^2) | O(n) | O(1) | 稳定 |
快速排序(不稳定 归位)
- 首先设定一个基准值,通过该基准值将数组分成左右两部分,将小于或等于基准值的数据放到数组的左边,将大于或等于基准值的数据放到数组的右边
- 此时左边部分中各元素都小于或等于基准值,而右边部分中各元素都大于或等于基准值,然后左边和右边的数据又可以分别独立排序。
- 对于左侧的数组数据,又可以再取一个基准值,将该部分数据分成左右两部分,同样在左边放置较小值,右边放置较大值,右侧的数组数据也可以做类似处理
- 重复上述过程,可以看出,这是一个递归定义
- 通过递归将左侧部分排好序后,再递归排好右侧部分的顺序。当左侧和右侧两个部分的数据排完序后,整个数组的排序也就完成了。
排序算法 | 平均时间复杂度 | 最坏时间复杂度 | 最好时间复杂度 | 空间复杂度 | 稳定性 |
---|---|---|---|---|---|
快速排序 | O(nlog2^n) | O(n^2) | O(nlog2^n) | O(log2^n) | 不稳定 |
归并排序(稳定 不归位)
- 申请空间,使其大小为两个已经排序序列之和,该空间用来存放合并后的序列
- 设定两个指针,最初位置分别为两个已经排序序列的起始位置
- 比较两个指针所指向的元素,选择相对小的元素放入到合并空间,并移动指针到下一位置
- 重复上一步 直到某一指针达到序列尾
- 将另一序列剩下的所有元素直接复制到合并序列尾
排序算法 | 平均时间复杂度 | 最坏时间复杂度 | 最好时间复杂度 | 空间复杂度 | 稳定性 |
---|---|---|---|---|---|
归并排序 | O(nlog2^n) | O(nlog2^n) | O(nlog2^n) | O(n) | 稳定 |
十四、算法
算法 | 场景 |
---|---|
分治法 | 将复杂问题分解成若干规模相同的子问题 |
动态规划法 | 类似于分治法。但具有最优子结构性质和重叠子问题性质 |
贪心法 | 不从整体考虑只求当前局部最优解 |
回溯法 | 达不到(最优)目标,就退回再走 |
回溯法-N皇后问题
深度优先策略
分治法
分治法的设计思想是将一个难以直接解决的大问题分解成一些规模较小的相同问题,以便各个击破,分而治之
(1)分解。将原问题分解成一些列子问题
(2)求解。递归地求解各个子问题。若子问题足够小,则直接求解
(3)合并。将子问题的解合并成原问题的解
动态规划法
动态规划与分治法不同的是:
动态规划法分解得到的子问题不是独立的
动态规划是全局最优解
(1)找出最优解的性质,并刻画其结构特征
(2)递归地定义最优解的值
(3)以自底向上的方式计算出最优值
(4)根据计算最优值得到的信息,构造一个最优解
具有以下两个性质,可以用动态规划法求解:
(1)最优子结构
(2)重叠子问题
0-1背包问题
时间空间复杂度O(N*W)
贪心法
所做出的选择只是在某种意义上的局部最优
并不能保证总能获得全局最优,但通常能得到较好的近似最优解
具有以下两个性质,可以用贪心法求解:
(1)最优子结构
(2)贪心选择性质
部分背包问题
分支限界法
以广度优先或最小耗费优先