funnycoding / blog

A Github Issue Blog
22 stars 0 forks source link

《On Java8》 1. 对象的概念 #2

Open funnycoding opened 4 years ago

funnycoding commented 4 years ago

第一章 对象的概念

第一章 对象的概念

第一章 对象的概念

这章是一个相当概括的章节,作者从高维度的视角概述了什么是面向对象,算是一个总结,适合看完书或者对OOP已经有一定的实践的人来看,你会发现很多概念你和作者都所见略同,同时查漏补缺,完善你自己的知识结构体系

> "我们没有意识到管用语言的机构有多大的力量。可以毫不夸张的说,它通过语义反应机制奴役我们。语言表现出来并在无意识中给我们留下深刻的印象的结构会自动投射到我们周围的世界。" > 计算机革命的起源来自机器。编程语言就像是那台机器。**它不仅是我们思维放大的工具与另一种表达媒介,更像是我们思想的一部分。** > > 面向对象编程是一种编程思维方式和编码架构。**本章讲述 OOP 的基本概述。** > #### 抽象 > 程序员需要建立起**机器模型**(解决方案空间)—— 实际解决的问题模型(问题空间) 之间的联系。 > > > > 所有编程语言都提供抽象机制。从某种问题来说,问题的复杂度直接取决于抽象的类型和质量。这里的类型指的是:【抽象的内容是什么】。 > > Alan Kay 总结了面向对象的五大特征,我们可理解纯粹的面向对象程序设计方法是什么样的: > > 1. **万物皆对象**。你可以将对象想象成一种特殊的变量。它存储数据,但可以在你对其发出请求时执行本身的操作。【理论上讲,你总是可以从要解决的问题身上抽象出概念性的组件,然后在程序中将其表示为一个对象。】 > 2. **程序是一组对象,通过消息传递来告知彼此该做什么。**要求调用一个对象的方法,你需要向该对象发送消息。【参数的传递,方法的调用】 > 3. **每个对象都有一种类型。**根据语法,每个对象都是某个类的一个实例。其中**类(Class)是类型(Type) 的同义词**,一个类最重要的特征就是"能将什么消息发给它" > 4. **每个对象都有自己的存储空间,可容纳其他对象。**或者说,通过封装现有对象,可制作出新型对象。【组合】所以尽管对象的概念非常简单,但在程序中却可达到任意高的复杂程度。 > 5. **同一类所有对象都能接收相同的消息。**这实际是别有含义的一种说法。由于类型“圆(Cricle)”的一个对象也属于类型为 形状(shape)的一个对象,所以一个圆完全能接收发送给“形状”的消息。这意味着可以让程序代码统一指挥“形状“ ,令其自动控制所有符合 “形状” 描述的对象,其中自然包括“圆”。这一对象称为对象的可替换性,是 OOP 最重要的概念之一。【**也就是多态**】 > > Grady Booch 提供了对对象更简洁的描述:一个对象具有自己的**状态,行为和标识**。这意味着对象有自己的内部数据(提供**状态**)、**方法**(**产生行为**)并彼此区分(**每个对象在内存中有唯一的地址**) ### 接口 > 亚里士多德大概是第一个认真研究 "类型" 的哲学家。他曾提出过“鱼类和鸟类“这样的概念。所有对象都是唯一的,但同时也是具有相同的特性和行为的对象所归属的类的一部分。这种思想被首次应用于第一个面向对象编程语言 Simula-67,它在程序中使用基本关键字 class 来引入新的类型(class 和 type 通常可互换使用,有些人对它们进行了进一步区分,他们强调 type 决定了接口,而 class 是那个接口的一种特殊实现方式) > ### 多态 > 我们再处理类的层次结构时,通常把一个对象看成是它所属的基类。而不是把它当成具体类。通过这种方式,我们可以编写出不局限于特定类型的代码。在上个”形状“的例子中,”方法“操纵的是通用形状【直接调用基类对象的方法】,而不关心它们是 ”圆“,”正方形“,还是”三角形“,或是某种未定义的形状。所有形状都可以具有绘制,擦除,和移动的功能。因此”方法“向其中的任何代表”形状“的对象发送消息都不必担心对象如何处理信息。 > > 【这也就说明了为何子类override父类方法时,为何方法的修饰符不能小于父类方法,因为多态保证了使用父类引用调用方法该方法必须存在】 > > 这种能力改善了我们的设计,且减少了软件维护的代价。如果我们把派生的对象类型统一看成它本身的基类,编译器在编译时期就无法准确的知道什么”形状“被擦除,哪一种车在行驶,或者哪种鸟在飞行。这就是关键所在:当程序接收这种消息时,程序员并不想知道哪段代码会被执行。”绘图“方法可以平等地应用到每种可能的形状上,形状会依据自身的具体类型执行恰当的代码。 > > > > 如果不需要知道执行了哪部分代码,**那我们就能添加一个新的不同执行方式的子类而不需要更改调用它的方法**。那么编译器在不确定哪部分代码时是怎样做的呢? > > 如下图的例子 > ![](https://xuyanxin-blog-bucket.oss-cn-beijing.aliyuncs.com/blog/20200205233608.png) > BirdController 对象和通用 Bird 对象中,BirdController 不知道 Bird的具体确切类型却还能正常工作。 > > 从BirdConroller 的角度来看,这是很方便的,因为它不需要编写特别的代码来确定 Bird 的具体确定类型或行为。 那么在调用 move(); 方法时是如何保证发生正确的行为(鹅走路,飞,或游泳;企鹅走路或游泳)的呢? > > > > **这个问题的答案,是面向对象设计的妙诀**:在传统意义上,编译器不能进行函数调用。由非 OOP 编译器产生的函数调用会引起所谓的 **早期绑定**, 这个术语你可能从未听说过,不会想过其他的函数调用方式。 这意味着编译器生成对特定函数名的调用,该调用会被解析为将执行的代码的绝对地址。【也就是编译的时候具体的调用方法就已经被确定】 > > 通过继承,程序直到运行时才能确定代码的地址。【因为父类引用可以指向子类对象】,因此发消息给对象时,还需要其他一些方案。为了解决这个问题,面向对象语言使用 **后期绑定**的概念。当向对象发送消息时,被调用的代码直到运行时才确定。 > > **编译器确保方法存在,并对参数和返回值执行类型检查,但是它不知道要执行的确切代码。** > > 为了执行后期绑定,Java 使用了一个特殊的代码位来代替绝对调用。 这段代码使用对象中存储的信息来计算方法主体的位置。因此每个对象的行为根据特定代码位的内容而不同。当你向对象发送消息时,对象知道该如何处理这条消息。 > > 在某些语言中,必须显式地授予方法后期绑定属性的灵活性。例如 C++ 使用 virtual 关键字。 在这些语言中,默认情况下方法不是动态绑定的。 > > **在Java中,动态绑定是默认行为,不需要额外的关键字来实现多态性。** ### 本章小结: > 面向过程程序包含【数据定义】和【函数调用】。要找到程序的意图,你必须要在脑中建立一个模型,弄清函数调用和更底层的概念。这些程序令人困扰,因为它们的标识更多地面向计算机而不是我们要解决的问题,这就是我们再设计程序时需要中间表示的原因。 > > OOP在面向过程变成的基础上增加了很多新的概念,并且作者认为按照 Java 规范编写的程序会比面向过程的程序更容易被理解。 > ### 我的理解: 【这一章主要概述了面向对象的几个核心概念,包括抽象,接口,封装,复用,继承,多态,集合,对象创建与生命周期,异常处理,有价值的地方在于从笔者这么一个经验丰富的人的角度来看Java这门语言的核心概念】 ### 我的一句话概括: **面向对象,将问题抽象为对象,然后将对象以不同关系进行组合,数据在对象中流转,对象之间有可用调用的方法,最终解决问题。**