“纯粹”的面向对象程序设计方法
(1) 所有东西都是对象。可将对象想象成一种新型变量;它保存着数据,但可要求它对自身进行操作。理论上讲,可从要解决的问题身上提出所有概念性的组件,然后在程序中将其表达为一个对象。
(2) 程序是一大堆对象的组合;通过消息传递,各对象知道自己该做些什么。为了向对象发出请求,需向那个对象“发送一条消息”。更具体地讲,可将消息想象为一个调用请求,它调用的是从属于目标对象的一个子例程或函数。
(3) 每个对象都有自己的存储空间,可容纳其他对象。或者说,通过封装现有对象,可制作出新型对象。所以,尽管对象的概念非常简单,但在程序中却可达到任意高的复杂程度。
(4) 每个对象都有一种类型。根据语法,每个对象都是某个“类”的一个“实例”。其中,“类”(Class)是“类型”(Type)的同义词。一个类最重要的特征就是“能将什么消息发给它?”。
(5) 同一类所有对象都能接收相同的消息。这实际是别有含义的一种说法,大家不久便能理解。由于类型为“圆”(Circle)的一个对象也属于类型为“形状”(Shape)的一个对象,所以一个圆完全能接收形状消息。这意味着可让程序代码统一指挥“形状”,令其自动控制所有符合“形状”描述的对象,其中自然包括“圆”。这一特性称为对象的“可替换性”,是OOP最重要的概念之一。
对象的接口
“接口”(Interface)规定了可对一个特定的对象发出哪些请求。然而,必须在某个地方存在着一些代码,以便满足这些请求。这些代码与那些隐藏起来的数据便叫作“隐藏的实现”。控制程序员对对象内部访问的原因主要有二:(1)防止外部程序员接触到他们不该接触的东西,比如内部数据;(2)允许库设计人员修改内部结构,而不需要担心它对外部客户程序员使用的影响。
Java有三个显式的关键字private,protected,public和一个隐式的关键字friendly。它们在Java中的作用跟其它语言中的关键字类似,在此不再赘述。
继承:重新使用接口
当我们需要新建一个类,它拥有一个已有的类的所有对象并实现了一个已有类的所有功能,这样的话我们可以采用继承的方法来创建一个新类,用这个新类来继承已有类。继承不是完全“克隆”,而是在“克隆”的基础上进行扩展和修改。Java中继承是通过extends关键字来实现的。
使用继承时,相当于创建了一个新类。这个新类不仅包含了现有类型的所有成员(尽管private成员被隐藏起来,且不能访问),但更重要的是,它复制了基础类的接口。
将子类和父类区分开来一般有两种方法:(1)为子类添加新的功能(函数);(2)在子类中改变基础类中现有函数的实现,这相当于是在子类中对基础类中已有的东西进行修改,从而使得子类与基础类有不同的实现,也就是“尽管使用的函数接口未变,但是它的新版本具有不同的表现”。
等价与类似的关系
继承并不是只改变原基础类中的函数,否则的话继承的类与基础类就是完全相同的类型。这样的话可以认为基础类和衍生类具有“等价”关系。但在很多情况下,我们需要在新类型中加入新的接口元素,这时不仅扩展了接口,也创建了而一个新的类型,这种新类型仍然可以替换成基础类型,但是这种替换不是完美的,因为不可以在基础类中访问新的函数。这种关系就称为“类似”关系,它指的是新类型中拥有旧类型的接口,但是也包含了新的函数。
对象的创建和存在时间
针对对象的创建以及存在时间,不同的编程语言有不同的处理方式:(1)C++允许程序员做出选择,存储和存在时间可以在编写程序的时决定,只需将对象放在堆栈或者静态存储区即可。这种方法的好处是运行速度快,缺点是缺乏灵活性。(2)在一个内存池中动态创建对象,该内存池亦叫“堆”或者“内存堆”。若采用这种方式,除非进入运行期,否则根本不知道到底需要多少个对象,也不知道它们的存在时间有多长,以及准确的类型是什么。这些参数都在程序正式运行时才决定的。若需一个新对象,只需在需要它的时候在内存堆里简单地创建它即可。在内存堆里分配存储空间的时间比在堆栈里创建的时间长得多(在堆栈里创建存储空间一般只需要一个简单的指令,将堆栈指针向下或向下移动即可)。由于存储空间的管理是运行期间动态进行的,所以在内存堆里分配存储空间的时间比在堆栈里创建的时间长得多(在堆栈里创建存储空间一般只需要一个简单的指令,将堆栈指针向下或向下移动即可)。
单根结构
在面向对象的程序设计中,由于C++的引入而显得尤为突出的一个问题是:所有类最终是否都应从单独一个基础类继承。在Java中(与其他几乎所有OOP语言一样),对这个问题的答案都是肯定的,这种“单根结构”具有许多方面的优点。单根结构中的所有对象都有一个通用接口,所以它们最终都属于相同的类型。