第一章 对象导论(对象的概念)
计算机革命起源于机器,因此,编程语言的产生也是始于对机器的模仿。
面向对象程序设计(Object-oriented Programming,OOP)是计算机作为表达的组成部分。
1. 抽象
编程语言都提供抽象机制,可以认为,人们所能够解决的问题的复杂性直接取决于抽象的类型和质量。
命令式语言(例如:C语言)都是对汇编语言的抽象。但主要抽象在于仍需要在解决问题时要基于计算机的结构,而不是基于所要解决问题的结构来考虑,需要建立如下关联。 建立映射是费力的,使得程序难以编写,且维护代价高昂;
面向对象则向程序员提供表示问题空间中的元素更进一步,这种表达方式非常通用,使得程序员不会受限制于任何特定类型的问题。即元素在解空间中的表示称为:“对象”(Object);所以,OOP允许根据问题来**描述对象,**而不是根据解决方案来描述问题。
面向对象程序设计方式:
- 万物皆对象。将对象视为变量,储存数据,可以要求在对象自身上执行操作;
- 程序是对象的集合,通过发送消息来告知彼此所要做的。想要请求一个对象,就必须对该对象发送一条消息。具体的说就是,把消息想象为对某个具体对象方法的调用请求。
- 每个对象都有自己的由其他对象所构成的存储。即可以调用现有的包的方式来创建新类型的对象,因此,可以在程序中构建复杂的体系,同时将其复杂性隐藏在对象的简单性背后。
- 每个对象都拥有其类型。按照通用的说法,“每个对象都是一个类(class)的一个实例”,“类”即"类型"的同义词,每个类最重要的区别于其他类的特征就是“可以发送什么样的消息给它”
- 某一特定类型的对象都可以接收同样的方法。例如“圆形”类型对象同时也是“几何形”类型对象,所以一个“圆形”对象必定能够接受发送给“几何形”对象的消息。这种可替代性是OOP中最强有力的概念之一。
对于对象的描述:对象具有状态**(内部数据)、行为(方法)和标识(内存中唯一地址)。
2. 对象
2.1 接口
所有的对象都是唯一的,但同时也是具有相同的特性和行为的对象所归属的类的一部分。程序中使用基本关键字class来引入新的类型(class 和 type 通常可互换使用,有些人对它们进行了进一步区分,他们强调 type 决定了接口,而 class 是那个接口的一种特殊实现方式))
在面向对象的程序设计中,尽管我们真正要做的是新建各种各样的数据“类型”(Type),但几乎所有面向对象的程序设计语言都采用了class
关键字。当你看到 “type” 这个词的时候,请同时想到class
;反之亦然。
进行面向对象的程序设计时,面临的最大一项挑战是:如何在“问题空间”(问题实际存在的地方)的元素与“方案空间”(对实际问题进行建模的地方,如计算机)的元素之间建立理想的“一对一”的映射关系。对象发出的请求是通过它的“接口”(Interface)定义的,对象的“类型”或“类”则规定了它的接口形式。“类型”与“接口”的对应关系是面向对象程序设计的基础。例如电灯泡;
2.2 服务提供
对象可以看作“服务的提供者”,如电灯泡提供on() 打开、off()关闭等服务;
在良好的面向对象设计中,每个对象功能单一且高效。这样的程序设计可以提高我们代码的复用性,同时也方便别人阅读和理解我们的代码。
2.3 封装
封装(encapsulation)即信息隐蔽。它是指在确定系统的某一部分内容时,应考虑到其它部分的信息及联系都在这一部分的内部进行,外部各部分之间的信息联系应尽可能的少。
Java中有三种显示关键字(public
、protected
、protected
)来设置访问权限,以及一种隐式(default
)默认访问权限;
修饰符 | 当前类 | 同包 | 子类 | 其他包 |
---|---|---|---|---|
public(公开) | √ | √ | √ | √ |
protected(受保护) | √ | √ | √ | × |
default(默认) | √ | √ | × | × |
protected(私有) | √ | × | × | × |
2.4 复用
- 组合(Composition)经常用来表示“拥有”关系(has-a relationship)。例如,“汽车拥有引擎”。
- 聚合(Aggregation)动态的组合。
3. 继承
-
继承: 继承是以现有类为基础, 复制现有类, 并对其的副本进行修改来创建新的类;
-
继承关系中,一个基类保函了其所有导出的所有类型的共性和行为
-
继承描绘的是将问题空间拆分为子空间的层次结构
-
继承支持对于基类功能的修改方式有两种:添加,覆盖
-
继承就是一替代关系:
- 最完美的替代关系是完全替代,此时基类和导出类之间是
is-a
关系,在这种情况下可以使用覆盖进行类的导出; - 不那么完美的替代关系是
is-like-a
关系,一般伴随者接口的增加,在这种情况下使用覆盖和添加;
- 最完美的替代关系是完全替代,此时基类和导出类之间是
-
Java 单根继承的设计结构:
- 单根继承:所有的类都有同一个最上层的基类,所有对象都有一个共有的接口;
- 单根继承的意义:方便为所有对象提供基础的功能,eg:Java中的Exception处理;
- 单根继承的Java实现:所有类的顶级基类都是
Object
类型;
4. 多态
多态:当将一个对象作为其基类进行对待时,动态的调整其功能的实现细节,以保证子类型对象状态和服务的正确性
5. 集合
“集合”(也可称之为"容器")这种类型的对象可以存储任意类型、数量的其他对象。
- Java中集合的结构设计:
- 集合用于解决的问题:当不清楚需要存储的对象的具体类型, 对象存活时间, 对象存储方式的时候, 能够以较为简单的方式通过标准库组件进行使用;
- Java容器的设计: 从整体上来讲, 容器就是一块可以用于存放数据的存储空间. 但是为了不同的存储方式, 设计了诸如
LinkedList
ArrayList
,Set
Map
等组织方式. 通过参数化类的形式避免内存数据的类型的丢失
- Java中集合使用参数化的原因
- 避免数据取出时发生类型的丢失;
- 避免耗时且危险的向下转型,通过编译器的展开处理为静态类型;
- 可以认为是以扩大运行文件大小的方式减小危险操作的频率;
6. 对象的创建与生命周期
- Java采用了完全动态内存的分配方式 – 所有的对象存放于堆中
- 通过GC(垃圾回收机制)对对象的引用进行标记, 进行自动的内存释放
7. 异常处理
- 异常处理在代码格式上是使用一个类进行抛出
- 异常处理分为两个步骤: 抛出异常, 捕获并处理异常
- 异常处理与C++中返回状态的区别:
- 异常对于正常代码逻辑入侵较小
- 返回状态可以被人为忽略但是异常必须经过处理
- 异常的捕获是与正常执行路径并行的一条路径, 独立于业务主逻辑
- 异常在Java中处理为对象的形式, 但是其并不是OOP概念, 而是类似于信号量的进程间通讯的概念
8. 并发编程
- 进行并发的目的是提高应用的实时响应能力
- Java中使用线程进行并发
- 当操作系统支持多处理器时, Java的多线程并发可以变化为多核并行
- Java对于并发与并行中需要使用的锁等其他功能有库进行支持