一、对象导论
1.抽象过程
Alan Kay曾经总结了第一个成功的面向对象语言、同时也是Java所基于的语言之一的Smalltalk的五个基本特性,这些特性表现了纯粹的面向对象程序设计方式
1)万物皆对象。
2)程序是对象的集合,它们通过发送消息来告知彼此所要做的。要想请求一个对象,就必须对该对象发送一条消息。更具体的说,可以把消息想象为对某个特定对象的方法的调用请求。
3)每个对象都有自己的由其他对象所构成的存储。换句话说,可以通过创建包含现有对象的包的方式来创建新类型的对象。
4)每个对象都拥有其类型。按照通用的说法,“每个对象都是某个类(class)的一个实例(instance)”。每个类最重要的区别于其他类的特性就是“可以发送什么样的消息给它”。
5)某一特定类型的所有对象都可以接收同样的消息。
2.每个对象都有一个接口
所有对象都是唯一的,但同时也是具有相同的特性和行为的对象所属类的一部分。
每个对象都只能满足某些请求,这些请求由对象的接口(interface)所定义。接口确定了对某一特定对象所能发出的请求。但是,程序中必须有满足这些请求的代码。这些代码和隐藏的数据一起构成了实现。
3.每个对象都提供服务
将对象想象成是”服务提供者“。这样做的好处之一:有助于提高对象的内聚性。高内聚是软件设计的基本质量要求之一:这意味着一个软件构件的各个方面”组合“的很好。
我们经常遇到的问题是,将过多的功能都塞在一个对象里。在良好的面向对象设计中,每个对象都可以很好地完成一项任务,但是它并不试图做更多的事。
4.被隐藏的具体实现
将程序开发人员按照角色分为**类创建者**和**客户端程序员**。客户端程序员的目标是收集各种用来实现快速应用开发的类。类创建者的目标是创建类,这种类只向客户端程序员暴露必需的部分,而隐藏其他部分。
访问控制的第一个存在原因就是让客户端程序员无法触及他们不应该触及的部分,让他们清晰地应用服务;第二个原因就是允许库设计者可以改变类内部的工作方式而不用担心影响到客户端程序员。
5.复用具体实现
组合:使用现有的类合成新的类,经常被视为”has-a”关系。
创建新类时优先选择组合。
6.继承
可以创建一个基类型来表示系统中某些对象的核心概念,从基类型中导出其他类型,来表示此核心可以被实现的各种不同方式。
所有可以发送给基类对象的消息同时也可以发送给导出类对象。由于通过发送给类的消息的类型可知类的类型,所以这也就意味着导出类与基类具有相同的类型。
有两种方法可以使基类与导出类产生差异。第一种:直接在导出类中添加新方法。这意味着基类不能满足你的所有需求,因此必须添加新方法;第二种是改变基类的方法,即覆盖(override)。
“是一个”与“像是一个”关系
is-a:继承只覆盖基类的方法,结果可以用一个导出类对象来完全替代一个基类对象。被视为纯粹替代。
has-a:新类型具有旧类型的接口,但还包含其他方法,基类无法访问新添加的方法。
7.伴随多态的可互换对象
在处理类型的层次结构时,通常想把一个对象不当作它所属的特定类型来对待,而是将其当作其基类的对象来对待。这样我们能编写出不依赖特定类型的代码。
一个非面向对象编程的编译器产生的函数调用会引起所谓的前期绑定,这意味着编译器将产生对一个具体函数名字的调用,而运行时将这个调用解析到将被执行的代码的绝对地址。
面向对象程序设计语言使用了后期绑定的概念。当对象发送消息时,被调用的代码直到运行时才能确定。编译器确保被调用方法的存在,并对调用参数和返回值执行类型检查,但是并不知道将被执行的确切代码。为了执行后期绑定,Java使用一小段特殊的代码来代替绝对地址调用。这段代码使用在对象存储的信息来计算方法体的地址。这样,根据这一小段代码的内容,每一个对象都可以具有不同的行为表现。当向一个对象发送消息时,该对象就能够知道这条消息应该做些什么。
把导出类看做是它的基类的过程称为向上转型。
8.单根继承结构
所有类都继承自Object。
单根继承结构保证所有对象都具备某些功能。
单根继承结构使垃圾回收器的实现变得容易得多。
9.容器
存储不定数量的对象。
参数化类型
Java SE5之前,容器存储的对象都只具有Java中的通用类型:Object。因为Object可以存储一切东西。但这种做法在涉及具体应用时则很危险,因为向下转型是不安全的,程序员需要花更多精力来处理正确的转型。
参数化类型机制则可以解决此问题。参数化类型就是一个在编译中可以自动定制作用于特定类型上的类。称之为范型。
10.对象的创建和生命期
怎样才能知道何时销毁这些对象?当处理完某个对象之后,系统某个部分可能还在处理它。
对象的数据位于何处?怎样控制对象的生命周期?这里有两种方式:
- 第一种是追求效率,对象的存储空间和生命周期可以在编写程序时确定,通过将对象置于堆栈(它们有时被称为自动变量或限域变量)或静态存储区域内来实现。
- 第二种是在被称为堆的内存池中动态地创建对象。这样,直到运行时才知道需要多少对象、生命周期以及具体类型。
因为存储空间是在运行时被动态管理的,所有需要大量的时间在堆中分配存储空间,这可能要远远大于在堆栈中创建存储空间的时间。在堆栈中创建存储空间和释放空间通常各需要一条汇编指令即可,分别对应将栈顶指针向下移动和将栈顶指针向上移动。创建堆存储空间的时间依赖于存储机制的设计。
动态方式有这样一个一般性的逻辑假设:对象趋向于变得复杂,所以查找和释放存储空间的开销不会对对象的创建造成重大冲击。
Java完全采用了动态内存分配方式。每当想要创建新对象时,就要使用new关键字来构建此对象的动态实例。
对于允许在堆栈上创建对象的语言,编译器可以确定对象的存活时间,并可以自动销毁它。然而,如果是在堆上创建对象,编译器就会对它的生命周期一无所知。因此,Java提供了被称为“垃圾回收器”的机制,它可以自动发现对象何时不再被使用,并继而销毁它。
11.异常处理:处理错误
大多数错误处理机制的主要问题在于,它们都依赖于程序员自身的警惕性,这种警惕性来源于一种共同的约定,而不是编程语言所强制的。
异常处理直接把错误处理直接置于编程语言中,有时甚至置于操作系统中。
注意,异常处理并不是面向对象语言的特征。
12.并发编程
在计算机编程中有一个基本概念,就是在同一时刻处理多个任务的思想。许多程序设计问题都要求,程序能够停下正在做的工作,转而处理某个其他问题,然后再返回主进程。
我们只需把任务切分成多个可独立运行的部分,从而提高程序的响应能力。在程序中,这些彼此独立运行的部分称之为线程,上述概念被称为“并发”。
但有一个隐患:共享资源。如果有多个并行任务都要访问同一项资源,那么就会出问题。解决问题的方法是:某个任务锁定某项资源,完成其任务,然后释放资源,使其他任务可以使用这项资源。
13.Java与Internet
待补。。。。看不懂
14.总结
Java程序中只包含以下两部分内容:用来表示问题空间概念的对象(而不是有关计算机表示方式的相关内容),以及发送给这些对象的用来表示在此空间内的行为的消息。
这一章的内容很广泛,还需要全面学习之后再回来总体把握精髓。