Java编程思想学习笔记
简单记录下学习《Java编程思想》这本书的过程。
第一章 对象导论
1.1抽象过程
所有编程语言都提供抽象机制。汇编语言:对底层 机器的轻微抽象;命令式语言:对汇编语言的抽象(如BASIC、 FORTRAN、C)。
解空间:对问题建模的地方,一般是计算机;
问题空间:问题存在的地方,一般是一项业务(也就是现实生活中的问题)。
程序员必须建立这两个空间之间的关联,但是这种关联的建立往往比较困难。产生了面向对象编程的方式,这种表示方式很通用,不限制于特定的问题。
面向对象思想的实质:程序通过添加新类型的对象来使自己适应某个特定问题——OOP允许根据问题来描述问题,而不是根据运行这个解决方案的计算机的状况来描述问题(相当于将问题具象化,方便编程人员从现实生活而不是计算机的结构对问题进行理解并进行建模)
对象:元素及其在问题空间中的表示。
面向对象语言(Java)的五个基本特点:
(1)万物皆为对象:将对象视为奇特的变量,可以存储数据,也可以进行一系列的动作,理论上可以将问题空间的一切物件抽象为对象;
(2)程序是对象的集合:程序之间通过发送消息来告知彼此需要做的动作,想请求一个对象,就必须向他发送一条消息(类似于传参);
(3)每个对象都有自己的由其他对象构成的存储:即可以通过创建含有包含现有对象的包的方式来创建新类型的对象(闭包的概念就是建立在这个特点之上);
(4)每个对象都有其类型:也即每个对象都是对应类的一个实例,类之间的区别就在于可以发送给这个类的消息的不同(也即传递参数列表的不同);
(5)某一特定类型的所有对象可以接受同样的消息(类似于类的继承、子类的概念),使得OOP具有了可替代性。
1.2每个对象都有一个接口
类(class)描述了具有相同特性(数据元素)和行为(功能,也就是函数)的对象集合,一个类实际上就是一个数据类型。
程序的编写人员可以通过定义新的类来适应问题,而不是直接使用用于表示机器中的存储单元的数据类型。问题也就被简化为“在问题空间的元素和解空间的对象之间建立一对一的映射。
接口确定了对某个特定对象可以发出的请求,这些请求必须在程序中有对应的满足其请求的代码(也就是类功能的实现)
1.3每个对象都提供服务
对象通常别理解为某项服务的提供者,创建对象的目的就在于提供服务、解决问题。
这种理解的好处在于有助于提高对象的内聚性,软件设计的基本质量要求之一是“高内聚低耦合”,换句话说,要尽量避免在一个对象中加入过多的不必要的功能。
1.4被隐藏的具体实现
程序的开发人员被分为类创建者和客户端程序员(也就是写库的和调库的),这种分类方法的好处在于客户端程序员可以通过创建对象来快速应用类,类创建者的目标在于构建类并实现必要的功能,这些类并不是全部对客户端程序员可见的,原因有:避免类中的关键部分被随意修改导致出现bug(保护目的)、方便客户端程序员进行调用(便捷目的)。
Java中的边界设定(访问控制)关键字:public、private、protected:
public:其后被定义的东西对任何人都可用;
private:其后的内容对任何人都不可访问(除类的创建者和类的内部方法);
protected:作用和private相似,差别在于继承的类可以访问protected成员,而不能访问private成员;
Java还有默认的访问权限,也即包访问权限,换句话说就是不能访问同个包之外的内容(除非import)。
1.5复用具体实现
最简单的复用类的方式:直接使用该类的一个对象、将这个类的一个对象置于另外一个新的类中(作为其数据成员)(使用现有的类合成新的类也被成为组合,若这个过程是动态发生,则被称为聚合)
eg.生产一台汽车需要引擎,那么就是对引擎这个类的一次复用
组合产生的新类的对象通常被声明为private,好处有二:使用新类的客户端程序员不能直接访问修改这些类,增强了安全性、类创建者可以在不干扰现有客户端代码的情况下对这些成员进行修改。
UML图表示:实心的菱形箭头指向新类
1.6继承
问题:创建一个类之后,若又有一个新类,功能和之前的类非常相似,还是要在创建一个新的类。这样就导致了代码编写工作的重复。
解决办法:以现有的类为基础,通过添加和修改这个类来创建新类——也就是继承。
当源类(基类、超类、父类)发生变动时,被修改的“副本”(导出类、继承类、子类)也会反映出这些变动
继承的UML图表示
两个相似的类之间也可以有区别,其中的某一个可能比另一个含有更多的特性(数据成员),可以处理更多的消息。继承通过基类型和导出类型来反映这种相似关系,编写程序时可以先构建一个基类型,基类型中包含所有导出类型共享的特性和行为,从基类型中导出其他不同的类型,表示核心可以被实现的各种不同的方式。
继承现有类的同时,也创建了新的类,新的类继承父类的所有成员(private类型的数据只是被隐藏起来,也是被继承了的)和接口,所有发给父类的消息也可以发给子类。前面提到,可以从发给类的消息的类型得知类的类型,所以子类和父类具有相同的类型(换句话说就是“厨余垃圾也是垃圾”),这就是通过继承而产生的等价性。
继承类之后,若不对复制来的接口进行实现,则收到相应的消息时会按照父类的实现方法来处理,这就没什么意义了。
基类和导出类产生区别的方法有二:
(1)直接在子类中添加父类中没有的新方法(java中表示继承的关键字extends也隐含着扩展的意思);
(2)覆盖(overriding):相当于在子类中重新实现父类的方法,表示使用不同的方式来处理传递来的信息。
考虑一种说法:继承应该只覆盖父类的方法。这种情况下,子类的接口完全复制自父类,他们的接口完全相同,尽管内部的处理方式不同,但是在外部调用者视角下,子类和父类是完全可以互相替代的,这也被称为“纯粹替代”,这种方式是有局限性的,事实上在大部分情况下我们都会在子类中实现新接口。
(1.18晚,读到P41)