《Java编程思想》之对象导论

一、抽象过程

所有的编程语言都提供抽象机制。可以认为,人们所能够解决的问题的复杂性直接取决于抽象的类型和质量。

所谓的”类型”是指”所抽象的是什么?”
汇编语言是对底层机器的轻微抽象。接着出现许多所谓的”命令式”语言(如BASIC、C等)都是对汇编语言的抽象。

以Alan Key曾经总结的第一个成功的面向对象语言、同时也是Java所基于的语言之一的Smalltalk的五个特性,这些特性表现了一种纯粹的面向对象程序设计方式:

(1)万物皆对象。

将对象视为奇特的变量,它可以存储数据,除此之外,你还可以要求它在自身上执行操作。理论上讲,你可以抽取待求解问题的任何概念化构件(狗、建筑物、服务等),将其表示为程序中的对象。

(2)程序是对象的集合,它们可以通过发送消息来告知彼此所要做的。

要想请求一个对象,就必须对该对象发送一条消息,更具体地说,可以把消息想象为对某个特定对象的方法的调用请求。

(3)每个对象都有自己的由其他对象所构成的存储。

换句话说,可以通过创建包含现有对象的包的方式来创建新类型的对象。因此,可以在程序中构建复杂的体系,同时将其复杂性隐藏在对象的简单性背后。

(4)每个对象都拥有其类型

按照通用的说法,”每个对象都是某个类的一个实例”,这里的”类”就是”类型”的同义词。每个类最重要的区别在于其他类的特性就是”可以发送什么样的消息给它”。

(5)某一特定类型的所有对象都可以接受同样的消息。

这是一句意味深长的表述,你在稍后便会看到。因为”圆形”类型的对象同时也是”几何形”类型的对象,所以一个”圆形”对象必定能够接受发送”几何形”对象的消息。这意味着可以编写与”几何形”交互并自动处理所有与几何形性质相关的事物的代码。这种可替代性是OOP中最强有力的概念之一。

Bootch对对象提出了一个更加简述的描述:对象具有状态、行为和标识。
这意味着每一个对象都可以拥有内部数据(它们给出了该对象的状态)和方法(它们产生行为),并且每一个对象都可以拥有内部数据(它们给出了该对象的状态)和方法(它们产生行为),并且每一个对象都可以唯一地与其他对象区分开来,具体说来,就是每一个对象在内存中都有一个唯一的地址。

二、每个对象都有一个接口

接口确定了某一特定对象所能发出的请求。但是,在程序中必须满足这些请求的代码。这些代码与隐藏的数据一起构成了实现。

从过程型编程的观点来看,这并不太复杂。在类型中,每一个可能的请求都有一个方法与之相关联,当向对象发送请求时,与之相关联的方法就会被调用。此过程通常被概括为:当某个对象”发送消息”(产生请求),这个对象便知道此消息的目的,然后执行对应的程序代码。

三、每个对象都提供服务

将对象看作是服务提供者好处如下:

  • 有助于提高对象的内聚性(高内聚)
  • 简化问题,更好的解决问题

四、被隐藏的具体实现

程序开发人员按照角色分为类创建者(那些创建新数据类型的程序员)和客户端程序员(那些在其应用中使用数据类型的类消费者)是大有裨益的。

客户端程序员的目标是收集各种用来快速应用开发的类。

类创建者的目标是构建类,这种类只向客户端程序员暴露必需的部分,而隐藏其他部分。

为什么要这样?

因为如果加以隐藏,那么客户端程序员将不能够访问它,这意味着类创建者可以任意修改被隐藏的部分,而不用担心对其他任何人造成影响。被隐藏的部分通常代表对象内部薄弱的部分,它们很容易被粗心的或不知内情的客户端程序员所毁坏,因此将实现隐藏起来可以减少程序bug。

访问控制存在的原因?

  • 让客户端程序员无法触及他们不应该触及的部分-这些部分对数据类型的内部操作来说是必需的,但并不是用户解决特定问题所需的接口的一部分(这对客户端程序员来说是一项服务,因为他们可以很容易地看出哪些东西可以清晰地分离并得以保护,那么你就可以轻而易举地完成这项工作。
  • 允许库设计者可以改变类内部的工作方式而不用担心会影响到客户端程序员(例如,你可能为了减轻开发任务而以某种简单的方式实现某个特定类,但稍后发现你必须改写它才能使其运行得更快。如果接口和实现可以清晰地分离并得以保护,那么你就可以轻而易举地完成这项工作)

Java用三个关键字在类的内部设定边界:public、private、protected。

public:表示紧随其后的元素对任何人都是可用的。

private:表示除类型创建者和类型的内部方法之外的任何人都不能访问。

protected:protected与private作用相当,差别仅在继承的类可以访问protected成员,但是不能访问private成员。

Java还有一种默认的访问权限,当没有使用前面提到的任何访问指定词时,它将发挥作用。
这种权限通常被称为包访问权限,因为在这种权限下,类可以访问在同一个包(库构件)中的其他类的成员,但是在包之外,这些成员如同private一样。

五、复用具体实现

最简单地复用某个类的方式就是直接使用该类的一个对象,此外也可以将那个类的一个对象置于某个新的类中。我们称其为”创建一个成员对象”。新的类可以由任意数量、任意类型的其他对象以任意可以实现新的类中想要的功能的方式所组成。因为是在使用现有的类合成新的类,所以这种概念被称为组合,如果组合是动态发生的,那么它通常被称为聚合。组合经常被视为”has-a”(拥有)关系,就像我们常说的”汽车拥有引擎”一样。

六、继承

is-a(是一个)和is-like-a(像是一个)

七、伴随着多态的可互换对象

八、单根继承

在单根继承结构中的所有对象都具有一个共用接口,所以它们归根到底都是相同的基本类型。
另一种(C++所能提供的)结构是无法确保所有对象都属于同一个基本类型。从向后兼容的角度看,这么做能够更好地适应C模型,而且受限较少,但是当要进行完全的面向对象程序员设计时,则必须构建自己的继承体系,使得它可以提供其他OOP语言内置的便利。

单根继承结构保证所有对象都具备某些功能。因此,所有对象都可以很容易地在堆上创建,而参数传递也得到了极大的简化。

单根继承结构使垃圾回收器的实现变得容易得多,而垃圾回收器正是Java相对C++的重要改进之一。由于所有对象都保证具有其类型信息,因此不会因无法确定对象的类型而陷入僵局。

九、容器

对容器有所选择,有两个原因:
第一、不同的容器提供了不同类型的接口和外部行为。堆栈相对队列就具备不同的接口和行为,也不同于集合和列表的接口和行为。它们之中的某种容器提供的解决方案比其他容器要灵活得多。
第二、不同的容器对于某些操作具有不同的效率。
例如ArrayList和LinkedList。
它们具有相同接口和外部行为的简单的序列,但是它们对某些操作所花费的代价却有天囊之别。在ArrayList中,随机访问元素是一个花费固定时间的操作;但是,对LinkedList来说,随机选取元素需要在列表中移动,这种代价是高昂的,访问越接近表尾的元素,花费的时间越长。而另一方面,如果想在序列中间插入一个元素,LinkedList的开销却比ArrayList要小。

参数化类型(又称泛型)

容器存储的对象都只具有Java中的通用类型:Object。单根继承结构意味着所有东西都是Object类型,所以可以存储Object的容器可以存储任何东西。这使得容器很容易被复用。

文章目录
  1. 1. 一、抽象过程
    1. 1.1. (1)万物皆对象。
    2. 1.2. (2)程序是对象的集合,它们可以通过发送消息来告知彼此所要做的。
    3. 1.3. (3)每个对象都有自己的由其他对象所构成的存储。
    4. 1.4. (4)每个对象都拥有其类型
    5. 1.5. (5)某一特定类型的所有对象都可以接受同样的消息。
  2. 2. 二、每个对象都有一个接口
  3. 3. 三、每个对象都提供服务
  4. 4. 四、被隐藏的具体实现
    1. 4.1. 为什么要这样?
    2. 4.2. 访问控制存在的原因?
  5. 5. 五、复用具体实现
  6. 6. 六、继承
  7. 7. 七、伴随着多态的可互换对象
  8. 8. 八、单根继承
  9. 9. 九、容器
    1. 9.1. 参数化类型(又称泛型)