欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

读书笔记——Java编程思想

程序员文章站 2024-03-17 18:23:58
...

第一章 对象导论

1.1抽象过程

(1)对象的定义:

我们将问题空间中的元素在解空间中的表示称为“对象”。

这种思想的实质是程序可以通过添加新类型的对象使自身自适应用于某个问题

(2)对象的特性:

Smaltalk(第一个成功的面向对象语言)的五个特性:

  • 万物皆对象
  • 程序是对象的集合,他们通过发送消息来告知彼此需要做的
  • 每个对象都具有类型
  • 某一个特定类型的所有对象都可以接受同样的消息

我们可以将对象视为特熟的变量,她可以存储数据,还可以执行一些操作,理论上讲,你可以抽取任何概念化构件(如猫、狗等)作为一个对象。
要想请求一个对象执行一些操作,就必须对对象发送一条消息,最终完成一个程序的操作。因此,程序将是很多对象之间相互请求的结果。
比如“圆形”是“几何图形”,因此,她必须能够接受发送给“几何图形”的消息。这种可替代性是oop中的强力概念

Booch对对象提出了一个更加简洁的描述

对象具有状态、行为和标识。

即,拥有内部数据、方法和“父类”。

1.2每个对象都有一个接口

每一个对象都智能满足某些请求,这些请求由对象的接口所定义,决定那个接口的便是类型。接口确定了对某一特定对象所能发出的请求。

1.3每个对象都提供服务

当正在试图开发或理解一个程序设计是,最好的方法之一就是将对象想象为“服务的提供者”。程序本身向客户服务,而这一目的由调用对象实现,而我们的目标就是去创建(或者在现有代码库中寻找)能够提供理想的服务来解决问题的一系列对象。

将对象看作是服务的提供者还可以有效的提高对象的内聚性,不仅在设计过程中非常有用,在其他人理解你的代码或重用某个对象时,如果他们看出了这个对象所能提供的服务的价值,他会调整对象以适应其设计的过程变得简单的多。

1.4被隐藏的具体实现

首先我们可以将程序开发人员对同一段代码的角色分为两种,一种是那些创建该类的程序员,类创建者;另一种是调用这个类的方法的客户端程序员,他们的目标是收集各种用来实现快速应用开发的类。

在通常情况下,类创建者们只会向客户端程序员暴露必要的部分,而隐藏其他对象内相对脆弱的部分,即通过接口和实现分离、访问权限来隐藏。这是为了防止客户端程序员粗心或不知情的情况下毁坏这以部分,而造成程序的bug。

因此,访问控制的第一个存在原因就是让客户端程序员无法触及他们不应该触及的部分。这部分对于数据类型的内部操作来说是必需的,但不是特定问题所需的接口的一部分。这样写可以让客户端程序员很容易的看出哪些对他来说很重要,而哪些可以忽略。

访问控制的第二个存在原因就是类库创建者可以改变类内部的工作方式而不用担心会影响到客户端程序员。接口和实现可以清晰的分离并得以保护,就可轻松的改变类的内部而不影响到类的使用。

Java使用了三个关键字在类的内部确定边界:public、private、protected。这些访问指定词(access specifier)决定了紧跟其后被定义的东西可以被谁使用。

  • public表示紧随其后的元素任何人都可以访问,都是可用的
  • private则表示除了类创建者和类内部方法以外都不能访问
  • protected与private作用相当,差别仅在于集成的类可以访问protected

Java中除了这三种权限外还有一种默认的访问权限,当没有使用前面提到的任何访问指定词时,类可以访问在同一个包中的其他类成员,但在包之外则和private一样。它一般被称为包访问权限

1.5 复用具体实现

1.5.1组合和聚合

在复用时,使用旧有的类组合成为新的类,称为组合,如果这一过程是动态的发生的,则被称为聚合。
组合经常被视为“has-a”(拥有)关系。就如同汽车拥有引擎。

组合而成的新类的成员通常都被声明为private,使得该类的客户端程序员不能访问他们,这使得你可以在不干扰 现有客户端代码的情况下,修改这些成员对象,实现动态修改代码

1.6继承

当继承现有类型时,也就创建了新的类型。这个新的类型不仅包括现有类型的所有成员(尽管private成员被隐藏了起来并且不可访问),更重要的是他复制了父类的接口。也就是说,所有发送给父类的消息,也都可以发送给子类。即父类与子类是有相同的类。即“一个圆形是一个几何图形”。

由于父类和子类拥有相同的基础接口,伴随接口必然有一些方法需要实现。这意味着子类与父类可能不仅仅有相同的类型,还可能有着相同的行为,这会使得子类的出现失去意义。

要使得子类与父类产生差异有两种方法,一种是在子类中添加新的方法这意味着父类不能满足与用户需求,因此需要添加更多的方法。但此时你需要思考,由于子类共有的方法很多,父类是否也需要这些方法。
读书笔记——Java编程思想
比如图中的“几何体”作为“圆形”、“正方形”、“长方形”的父类,是否也需要他们所共有的画出和擦除方法。

而另一种方法,则是在子类中改变父类的方法,被称为覆盖。想要覆盖方法,在子类中直接创建该类的新方法即可。

1.6.1 “是一个”与“像是一个”的关系

如果子类只是覆盖其父类的方法,而没有在其中添加父类没有的新方法,也就是他们是完全一样的类型,内含的是完全一样的接口,子类可以替代父类。那么这两个类之间的关系就是“是一个”的关系。

如果子类在父类的基础上添加了新的方法,两者之间不是完美的替代关系了(因为父类无法使用子类新添加的方法),则被称为“像是一个”的关系。

1.7伴随多态的可互换对象

在处理类型的层次结构时,我们经常会把一个类当做其父类来对待,这可以让人们可以编写出不依赖于特定类型的代码。比如之前“几何体“的例子,方法对象都是泛化(generic)的,我们并不关心在实际上它到底是圆形、正方形还是长方形又或者是什么未定义的形状。我们知道的是,所有的形状都可以绘出和擦除。

这种方式写出的代码不会受到类型新加的子类的影响,而天剑新的类型是扩展一个面向对象程序以便处理新情况的最常用的方式。也就是说,可以极大的降低我们的维护成本,提高可维护性。

但这样有一个弊端,在将子类作为泛化的父类看待时,编译器是不可能知道应该执行那一段代码的。在执行的过程中,将会动态的转换真正执行的代码。那么如果编译时编译器不知道具体执行时的代码,又该如何编译呢?

这个问题的答案,就是面向对象程序设计的最重要的秘诀:

编译器不可能产生传统意义上的函数调用

为了解决前述的问题,面向对象程序设计语言使用了后期绑定的概念。当对象发送消息时,被调用的代码知道运行时才能确定。为了执行后期绑定,Java使用了一小段特殊的代码来替代绝对地址调用。这段代码用于在对相中存储的信息来计算方法体的地址。在一些语言中,我们必须明确的声明希望某个方法具有后期绑定的特性,而在Java中,后期绑定是默认行为。

让我们用一组代码片来描述这一过程

    void doSomething(Shape shape){
        shape.draw();
        shape.erase();
    }

而在另一个方法中

Circle circle = new Circle();
Line line = new Line();
doSomething(circle);
doSomething(line);

我们可以看到,在这里,Circle被传到了预期接收Shape的方法中,而doSomething()方法将其视作了Shape处理。
这种将子类视作父类进行处理的过程称为向上转型

1.8单根继承结构

除了C++以外的所有OOP语言中所有的类都会有一个单一的基类(父类)。而在Java中,这一父类即是Object。

在单根继承结构中,所有对象都具有一个共用接口,所以他们基本都“像是一个”相同的类型。

单根继承结构保证所有对象都具备某些功能,因此你可以在每个对象上都执行某些基本的操作。而且,这以结构使得垃圾回收器的实现变得容易的多,而这一点正是Java对于其他语言的一个重大的优点。由于所有对象都保证具有其类型信息,因此不会因为无法确定对象的类型而陷入僵局。

1.9容器

1.9.1何为容器

持有对其他对象的引用的新的对象类型。也称作,集合

当然,你同样可以使用绝大部分语言都有的数组来完成这一功能。

1.9.2为何选择容器

从设计的观点来看,真正需要的容器金金金是一个可以被操作、从而解决问题的序列。如果单一的容器就可以解决问题,那么就没有谁及多种的必要了。
在这里,对于容器的选择有两个原因:

  • 不容的容器提供了不同类型的接口和行为
  • 不同的容器对于某些操作具有不同的效率
1.9.3参数化类型

(待续)