【Java编程思想】阅读笔记
对象的概念
启
“我们没有意识到惯用语言的结构有多大的力量。可以毫不夸张地说,它通过语义反应机制奴役我们。语言表现出来并在无意识中给我们留下深刻印象的结构会自动投射到我们周围的世界。” – Alfred Korzybski (1930)
作者第一章开头引用的话,毫不夸张。正如语言的差异,造就了不同地区人群文化与性格上的差异。英文语法严谨,欧美人考虑问题就偏线性化,在意因果关系;而汉语的语法相对松散,没有特别固定的口语说法,才造就了国人圆融的处世原则。
所以,不同的编程语言,同样也会对使用者的思维方式乃至最终编写出的程序产生影响。编程语言就是创建应用程序的思想结构。
Object-Oriented Programming,即OOP,面向对象编程。是一种编程思维方式和编码架构。
抽象
程序员需要在机器模型(“解决方式空间”)和实际解决的问题模型(“问题空间”)之间建立起一种关联。
汇编语言是对底层机器的轻微抽象。接着出现的“命令式”语言(如 FORTRAN,BASIC 和 C)是对汇编语言的抽象。这些偏底层抽象的语言要求我们着重考虑计算机的结构,而非问题本身的结构。
面向对象的程序设计在此基础上跨出了一大步,程序员可利用一些工具表达“问题空间”内的元素。将问题空间中的元素以及它们在解决方案空间的表示称作“对象”(Object)。当然,还有一些在问题空间没有对应的对象体。通过添加新的对象类型,程序可进行灵活的调整,以便与特定的问题配合。所以当你在阅读描述解决方案的代码时,也是在阅读问题的表述。
总之,OOP 允许我们根据问题来描述问题(着重于对解决问题的描述建模),而不是根据运行解决方案的计算机。
Smalltalk 作为第一个成功的面向对象并影响了 Java 的程序设计语言 ,其五大基本特征如下。(通过这些特征,我们可理解“纯粹”的面向对象程序设计方法是什么样的):
- 万物皆对象(将对象想象成一种特殊的变量。它存储数据,但可以在你对其“发出请求”时执行本身的操作)
- 程序是一组对象,通过消息传递来告知彼此该做什么
- 每个对象都有自己的存储空间,可容纳其他对象(通过封装现有对象,可制作出新型对象)
- 每个对象都有一种类型(一个类最重要的特征就是“能将什么消息发给它?”)
同一类所有对象都能接收相同的消息(这一特性称为对象的“可替换性”,是OOP最重要的概念之一)
接口
在面向对象的程序设计中,尽管我们真正要做的是新建各种各样的数据“类型”。创建好一个类后,可根据情况生成许多对象。随后,可将那些对象作为要解决问题中存在的元素进行处理。如何在“问题空间”(问题实际存在的地方)的元素与“方案空间”(对实际问题进行建模的地方,如计算机)的元素之间建立理想的“一对一”的映射关系,是主要的问题。
我们向对象发出的请求是通过它的“接口”(Interface)定义的,对象的“类型”或“类”则规定了它的接口形式。“类型”与“接口”的对应关系是面向对象程序设计的基础。服务提供
在开发或理解程序设计时,我们可以将对象看成是“服务提供者”。你的程序本身将为用户提供服务,并且它能通过调用其他对象提供的服务来实现这一点。我们的最终目标是开发或调用工具库中已有的一些对象,提供理想的服务来解决问题。开发前我们可以将实际问题一一分解,抽象成一组服务。
软件设计的基本原则是高内聚:每个组件的内部作用明确,功能紧密相关。每个对象都提供了一组紧密的服务。
在良好的面向对象设计中,每个对象功能单一且高效。这样的程序设计可以提高我们代码的复用性,同时也方便别人阅读和理解我们的代码。封装
编程的侧重领域划可以分为研发领域和应用领域。应用程序员调用研发程序员构建的基础工具类来做快速开发,研发程序员开发一个工具类,该工具类仅向应用程序员公开必要的内容,并隐藏内部实现的细节。
访问控制可以使类库的创建者(研发程序员)在不影响后者使用的情况下完善更新工具库。例如,我们开发了一个功能简单的工具类,后来发现可以通过优化代码来提高执行速度。假如工具类的接口和实现部分明确分开并受到保护,那我们就可以轻松地完成改造。
Java 有三个显式关键字和一个隐式默认关键字来设置类中的访问权限:public(公开)表示任何人都可以访问和使用该元素;
- private(私有)除了类本身和类内部的方法,外界无法直接访问该元素。private 是类和调用者之间的屏障。任何试图访问私有成员的行为都会报编译时错误;
- protected(受保护)类似于 private,区别是子类(下一节就会引入继承的概念)可以访问 protected 的成员,但不能访问 private 成员;
- default(默认)如果你不使用前面的三者,默认就是 default 访问权限。default 被称为包访问,因为该权限下的资源可以被同一包(库组件)中其他类的成员访问。
继承
通过使用对象,类型层次结构成为了主要模型,因此你可以直接从真实世界中对系统的描述过渡到用代码对系统进行描述。
代码和设计方案的复用性是面向对象程序设计的优点之一。从现有类型继承创建新类型。这种新类型不仅包含现有类型的所有成员(尽管私有成员被隐藏起来并且不可访问),而且更重要的是它复制了基类的接口。也就是说,基类对象接收的所有消息也能被派生类对象接收。根据类接收的消息,我们知道类的类型。单继承结构
在 Java 中,最终基类的名字是 Object。
单继承的结构使得垃圾收集器的实现更为容易。这也是 Java 在 C++ 基础上的根本改进之一。
由于运行期的类型信息会存在于所有对象中,所以我们永远不会遇到判断不了对象类型的情况。这对于系统级操作尤其重要,例如异常处理。同时,这也让我们的编程具有更大的灵活性。多肽
以基类为蓝本派生新类型是扩展面向对象程序以处理新情况的,封装变化的基本方法之一。 例如,你可以通过通用的“形状”基类派生出新的“五角形”形状的子类,而不需要修改通用”形状”基类的方法。
在传统意义上,编译器不能进行函数调用。由非 OOP 编译器产生的函数调用会引起所谓的早期绑定,这意味着编译器生成对特定函数名的调用,该调用会被解析为将执行的代码的绝对地址。
通过继承,程序直到运行时才能确定代码的地址,因此发送消息给对象时,还需要其他一些方案。为了解决这个问题,面向对象语言使用后期绑定的概念。当向对象发送信息时,被调用的代码直到运行时才确定。编译器确保方法存在,并对参数和返回值执行类型检查,但是它不知道要执行的确切代码。
把子类当成其基类来处理的过程叫做“向上转型”(upcasting)。在面向对象的编程里,经常利用这种方法来给程序解耦。
发送消息给对象时,如果程序不知道接收的具体类型是什么,但最终执行是正确的,这就是对象的“多态性”(Polymorphism)。面向对象的程序设计语言是通过“动态绑定”的方式来实现对象的多态性的。为了执行后期绑定,Java 使用一个特殊的代码位来代替绝对调用。这段代码使用对象中存储的信息来计算方法主体的地址(此过程在多态性章节中有详细介绍)。因此,每个对象的行为根据特定代码位的内容而不同。
在 Java 中,编译器和运行时系统会负责对所有细节的控制,动态绑定是默认行为,不需要额外的关键字来实现多态性(C++ 使用 virtual 关键字)。集合
创建一个新类型的对象来引用、容纳其他的对象,在 Java 中可以使用“集合”(Collection)。(也可称之为“容器”,但“集合”这个称呼更通用。)
“集合”这种类型的对象可以存储任意类型、数量的其他对象。它能根据需要自动扩容,开发者不用关心过程是如何实现的。
我们可以一开始使用 LinkedList 构建程序,在优化系统性能时改用 ArrayList。通过对 List 接口的抽象,我们可以很容易地将 LinkedList 改为 ArrayList。
在 Java 5 泛型出来之前,集合中保存的是通用类型 Object。Java 5 版本支持了参数化类型机制,称之为“泛型”(Generic)。泛型是 Java 5 的主要特性之一。对象的生命周期
在 C++ 程序设计中,当使用完一个对象后,必须明确将其删除。 在 C++ 设计中采用的观点是效率第一,因此它将选择权交给了程序员。为了获得最大的运行时速度,程序员可以在编写程序时,通过将对象放在栈(Stack,有时称为自动变量或作用域变量)或静态存储区域(static storage area)中来确定内存占用和生存时间。
Java 使用动态内存分配。每次创建对象时,使用 new 关键字构建该对象的动态实例。在堆内存(Heap)中动态地创建对象,直到程序运行我们才能确定需要创建的对象数量、生存时间和类型。Java 的内存管理是建立在垃圾收集器上的,它能自动发现对象不再被使用并释放内存。异常处理机制
Java 从一开始就内置了异常处理,这是 Java 语言唯一接受的错误报告方法。如果没有编写适当的异常处理代码,运行时出现错误将会收到一条编译时错误消息。
异常处理机制将程序错误直接交给编程语言甚至是操作系统。“异常”(Exception)是一个从出错点“抛出”(thrown)后能被特定类型的异常处理程序捕获(catch)的一个对象。它不会干扰程序的正常运行,仅当程序出错的时候才被执行。“异常机制”提供了一种可靠地从错误状况中恢复的方法,使得我们可以编写出更健壮的程序。小结
设计良好的 Java 程序代码更容易被人阅读理解。由于 Java 类库的复用性,通常程序要写的代码也会少得多。
转载自个人博客:https://fusangjie.club/