Thinking in Java 笔记

来源:互联网 发布:mac终端更改系统时间 编辑:程序博客网 时间:2024/05/17 04:35

第一章 对象入门

1.1抽象的进步

所有编程语言的最终目的都是提供一种“抽象”的方法。解决问题的复制程度直接取决于抽象的种类及质量。
汇编语言是对基础机器的少量抽象,“命令式”语言是对汇编语言的一种抽象。原理依然要求我们着重考虑计算机的结构,而非考虑问题本身的结构。

我们将问题空间中的元素以及它们在方案空间的表示物称作“对象”(Object)。

完全面向对象的程序设计方法的特定:
1、所有东西都是对象。
2、程序是一堆对象的集合。
3、每个对象都有自己的存储空间,可容纳其他对象,对象的复制程度可达到任意高。
4、每个对象都有一种类型。即每个对象都是一个“类”的“实例”。
5、同一类所有对象都可接收相同的消息。对象的“可替换性”。

1.2对象的接口

我们向对象发出的请求是通过它的“接口”(Interface)定义的,对象的“类型”或“类”则规定了它的接口形式。

这里写图片描述
在这个例子中,类型/类的名称是 Light,可向 Light 对象发出的请求包括包括打开(on)、关闭(off)、变得更明亮(brighten)或者变得更暗淡(dim)。通过简单地声明一个名字(lt),我们为 Light 对象创建了一个“句柄”。然后用new关键字新建类型为 Light 的一个对象。再用等号将其赋给句柄。为了向对象发送一条消息,我们列出句柄名(lt),再用一个句点符号(.)把它同消息名称(on)连接起来。

1.3实现方法的隐藏

“接口”(Interface)规定了可对一个特定的对象发出哪些请求。然而,必须在某个地方存在着一些代码,以便满足这些请求。这些代码与那些隐藏起来的数据便叫作“隐藏的实现”。

有两方面的原因促使我们控制对成员的访问:
1、防止程序员接触他们不该接触的东西——通常是内部数据类型的设计思想。我们向用户提供的实际是一种服务,用户只需操作接口即可。
2、允许库设计人员修改内部结构,不用担心它会对客户程序员造成什么影响。

ava权限修饰符public、protected、private置于类的成员定义前,用来限定对象对该类成员的访问权限。
这里写图片描述

1.4 方案的重复使用

代码或设计方案的重复使用是面向对象的程序设计提供的最伟大的一种杠杆。

1.5继承:重新使用接口

使用继承时,相当于创建了一个新类。这个新类不仅包含了现有类型的所有成员(尽管private 成员被隐藏起来,且不能访问),但更重要的是,它复制了基础类的接口。也就是说,可向基础类的对象发送的所有消息亦可原样发给衍生类的对象。根据可以发送的消息,我们能知道类的类型。这意味着衍生类具有与基础类相同的类型!

1.5.1 改善基础类

为衍生类的函数建立一个新定义

1.5.2 等价与类似关系

等价关系:继承只是改善了原基础类的函数。->圆就是一种几何形状。
类似关系;衍生类加入了新的基础元素,新类型不完美替换成基础类型,不能访问新函数。

1.6多形对象的互换使用

这里写图片描述
衍生类的对象可以当作基础类的一个对象对待,我们为“几何形状”新类型编写的代码会象在旧类型里一样良好地工作。所以说程序具备了“扩展能力”,具有“扩展性”。

void doStuff(Shape s){    s.erase();    //...    s.draw();}

这个函数完全独立于它任何特定类型的对象,可与任何“几何形状”(Shape)通信。

Circle c=new Circle();Triangle t=new Triangle();Line l=new Line();doStuff(c);doStuff(t);doStuff(l);

doStuff(c);
此时,一个Circle(圆)句柄传递给一个本来期待Shape(形状)句柄的函数。由于圆是一种几何形状,所有以doStuff()能正确地进行处理。也就是说doStuff()能发给一个Shape的消息,Circle也能接收。
我们将这种把衍生类型当作它的基本类型处理的过程叫作“Upcasting”(上溯造型)。其中,“cast”(造型)是指根据一个现成的模型创建;而“Up”(向上)表明继承的方向是从“上面”来的。

1.6.1 动态绑定

在doStuff()的代码里,最让人吃惊的是尽管我们没作出任何特殊指示,采取的操作也是完全正确和恰当的。我们知道,为Circle 调用draw()时执行的代码与为一个 Square或 Line 调用draw()时执行的代码是不同的。但在将draw()消息发给一个匿名 Shape时,根据 Shape句柄当时连接的实际类型,会相应地采取正确的操作。

1.6.2 抽象的基础类和接口

抽象类的一个对象,编译器就会阻止他们
无构造器
interface(接口)关键字将抽象类的概念更延伸了一步,它完全禁止了所有的函数定义。

1.7 对象的创建和存在时间

对象的创建和破坏方式,对象需要的数据存放位置,如何控制对象的“存在时间”?
C++认为程序的执行效率是最重要的,允许程序员作出选择。为获得最快的运行速度,存储以及存在时间可在编写程序时决定,只需将对象放置在堆栈(有时也叫作自动或定域变量)或者静态存储区域即可。这样便为存储空间的分配和释放提供了一个优先级。某些情况下,这种优先级的控制是非常有价值的。然而,我们同时也牺牲了灵活性,因为在编写程序时,必须知道对象的准确的数量、存在时间、以及类型。
第二个方法是在一个内存池中动态创建对象,该内存池亦叫“堆”或者“内存堆”。若采用这种方式,除非进入运行期,否则根本不知道到底需要多少个对象,也不知道它们的存在时间有多长,以及准确的类型是什么。由于存储空间的管理是运行期间动态进行的,所以在内存堆里分配存储空间的时间比在堆栈里创建的时间长得多。
C++允许我们决定是在写程序时创建对象,还是在运行期间创建。

1.7.1 集成与继承器

Java 也用自己的标准库提供了集合
如果想对集合中的一系列元素进行操纵或比较使用一个“继承器”(Iterator),它属于一种对象,负责选择集合内的元素,并把它们提供给继承器的用户。通过继承器的作用,集合被抽象成一个简单的序列。继承器允许我们遍历那个序列,同时毋需关心基础结构是什么。

1.7.2 单根结构

所有类最终都应从单独一个基础类继承。Java中这个终极基础类是“Object”。
单根结构中的所有对象都有一个通用接口,所以它们最终都属于相同的类型。

优点:
单根结构中所有对象都可以保证有一些特定的功能,对每个对象都能进行一些基础操作。
所有对象都在内存堆中创建,可以极大简化参数的传递。
可以更方便地实现垃圾收集器,与此相关的必要支持可安装在基础类中,而且便于垃圾收集器将消息发送给系统的任何对象。

C++没有采用单根结构,是在效率与控制权上权衡的结果。单根结构会带来程序设计上的一些限制。且不便于与C兼容。

1.7.3 集合库与方便使用集合

下溯造型是将父类对象转成子类对象的过程。
我们可以采用“参数化类型”,使容纳Object类型的集合“智能”化,如只接受Shape,而且只提取Shape。在 C++中,用于实现参数化类型的关键字是 template(模板)。Java 目前尚未提供参数化类型,但“generic”这个词已被Java“保留到将来实现”。

第二章 一切都是对象

纯粹OOP语言的标志是所有程序都是对象。

对象是一个一个的实例引用,这好比C中的地址指针,但好处是Java中不需要去人工释放这些引用指针,这由GC自动完成回收。

Java中的所有对象都必须通过new来创建,即分配内存空间。

保存的位置:

  • 寄存器:最快的区域,在处理器内部,无直接控制权。
  • 栈:位于常规RAM(随机访问寄存器)区域。堆栈指针若向下移,会创建新的内存;若向上移,则会释放那些内存。速度仅次于寄存器。创建程序时,Java 编译器必须准确地知道堆栈内保存的所有数据的“长度”以及“存在时间”。这是由于它必须生成相应的代码,以便向上和向下移动指针。Java 数据要保存对象句柄在桟,Java对象并不放其中。
  • 堆:也在RAM,保存Java对象。编译器不必知道要从堆里分配多少存储空间,也不必知道存储的数据要在堆里停留多长的时间。要求创建一个对象时,只需用new命令编制相关的代码即可。执行这些代码时,会在堆里自动进行数据的保存。
  • 静态存储:这儿的“静态”(Static)是指“位于固定位置”(尽管也在 RAM里)。程序运行期间,静态存储的数据将随时等候调用。可用static 关键字指出一个对象的特定元素是静态的。但 Java 对象本身永远都不会置入静态存储空间。
  • 常数存储:常数值通常直接置于程序代码内部。有的常数值需要严格地保护,所以可考虑置于只读存储器(ROM)。
  • 非RAM存储:非RAM 存储。若数据完全独立于一个程序之外,则程序不运行时仍可存在,并在程序的控制范围之外。其中两个最主要的例子便是“流式对象”和“固定对象”。对于流式对象,对象会变成字节流,通常会发给另一台机器。而对于固定对象,对象保存在磁盘中。即使程序中止运行,它们仍可保持自己的状态不变。对于这些类型的数据存储,一个特别有用的技巧就是它们能存在于其他媒体中。一旦需要,甚至能将它们恢复成普通的、基于RAM的对象。Java 1.1 提供了对Lightweight persistence 的支持。未来的版本甚至可能提供更完整的方案。
原创粉丝点击