网站建设注册密码咋弄,自己做网站需要买哪些东西,深圳坪山高级中学,汉中市建设工程质量安全监督站官网本章概要
单继承结构集合对象创建与生命周期异常处理其它
单继承结构
自从 C 引入以来#xff0c;一个 OOP 问题变得尤为突出#xff1a;是否所有的类都应该默认从一个基类继承呢#xff1f;这个答案在 Java 中是肯定的#xff08;实际上#xff0c;除 C 以外的几乎所有…本章概要
单继承结构集合对象创建与生命周期异常处理其它
单继承结构
自从 C 引入以来一个 OOP 问题变得尤为突出是否所有的类都应该默认从一个基类继承呢这个答案在 Java 中是肯定的实际上除 C 以外的几乎所有OOP语言中也是这样。在 Java 中这个最终基类的名字就是 Object。
Java 的单继承结构有很多好处。由于所有对象都具有一个公共接口因此它们最终都属于同一个基类。相反的对于 C 所使用的多继承的方案则是不保证所有的对象都属于同一个基类。从向后兼容的角度看多继承的方案更符合 C 的模型而且受限较少。
对于完全面向对象编程我们必须要构建自己的层次结构以提供与其他 OOP 语言同样的便利。我们经常会使用到新的类库和不兼容的接口。为了整合它们而花费大气力有可能还要用上多继承以获得 C 样的“灵活性”值得吗如果从零开始Java 这样的替代方案会是更好的选择。
另外单继承的结构使得垃圾收集器的实现更为容易。这也是 Java 在 C 基础上的根本改进之一。
由于运行期的类型信息会存在于所有对象中所以我们永远不会遇到判断不了对象类型的情况。这对于系统级操作尤其重要例如异常处理。同时这也让我们的编程具有更大的灵活性。
集合
通常我们并不知道解决某个具体问题需要的对象数量和持续时间以及对象的存储方式。那么我们如何知悉程序在运行时需要分配的内存空间呢
在面向对象的设计中问题的解决方案有些过于轻率创建一个新类型的对象来引用、容纳其他的对象。当然我们也可以使用多数编程语言都支持的“数组”array。在 Java 中“集合”Collection的使用率更高。也可称之为“容器”但“集合”这个称呼更通用。
“集合”这种类型的对象可以存储任意类型、数量的其他对象。它能根据需要自动扩容我们不用关心过程是如何实现的。
还好一般优秀的 OOP 语言都会将“集合”作为其基础包。在 C 中“集合”是其标准库的一部分通常被称为 STLStandard Template Library标准模板库。SmallTalk 有一套非常完整的集合库。同样Java 的标准库中也提供许多现成的集合类。
在一些库中一两个泛型集合就能满足我们所有的需求了而在其他一些类库Java中不同类型的集合对应不同的需求常见的有 List常用于保存序列Map也称为关联数组常用于将对象与其他对象关联Set只能保存非重复的值其他还包括如队列Queue、树Tree、栈Stack、堆Heap等等。从设计的角度来看我们真正想要的是一个能够解决某个问题的集合。如果一种集合就满足所有需求那么我们就不需要剩下的了。之所以选择集合有以下两个原因
集合可以提供不同类型的接口和外部行为。堆栈、队列的应用场景和集合、列表不同它们中的一种提供的解决方案可能比其他灵活得多。不同的集合对某些操作有不同的效率。例如List 的两种基本类型ArrayList 和 LinkedList。虽然两者具有相同接口和外部行为但是在某些操作中它们的效率差别很大。在 ArrayList 中随机查找元素是很高效的而 LinkedList 随机查找效率低下。反之在 LinkedList 中插入元素的效率要比在 ArrayList 中高。由于底层数据结构的不同每种集合类型在执行相同的操作时会表现出效率上的差异。
我们可以一开始使用 LinkedList 构建程序在优化系统性能时改用 ArrayList。通过对 List 接口的抽象我们可以很容易地将 LinkedList 改为 ArrayList。
在 Java 5 泛型出来之前集合中保存的是通用类型 Object。Java 单继承的结构意味着所有元素都基于 Object 类所以在集合中可以保存任何类型的数据易于重用。要使用这样的集合我们先要往集合添加元素。由于 Java 5 版本前的集合只保存 Object当我们往集合中添加元素时元素便向上转型成了 Object从而丢失自己原有的类型特性。这时我们再从集合中取出该元素时元素的类型变成了 Object。那么我们该怎么将其转回原先具体的类型呢这里我们使用了强制类型转换将其转为更具体的类型这个过程称为对象的“向下转型”。通过“向上转型”我们知道“圆形”也是一种“形状”这个过程是安全的。可是我们不能从“Object”看出其就是“圆形”或“形状”所以除非我们能确定元素的具体类型信息否则“向下转型”就是不安全的。也不能说这样的错误就是完全危险的因为一旦我们转化了错误的类型程序就会运行出错抛出“运行时异常”RuntimeException。后面的章节会提到 无论如何我们要寻找一种在取出集合元素时确定其具体类型的方法。另外每次取出元素都要做额外的“向下转型”对程序和程序员都是一种开销。以某种方式创建集合以确认保存元素的具体类型减少集合元素“向下转型”的开销和可能出现的错误难道不好吗这种解决方案就是参数化类型机制Parameterized Type Mechanism。
参数化类型机制可以使得编译器能够自动识别某个 class 的具体类型并正确地执行。举个例子对集合的参数化类型机制可以让集合仅接受“形状”这种类型的元素并以“形状”类型取出元素。Java 5 版本支持了参数化类型机制称之为“泛型”Generic。泛型是 Java 5 的主要特性之一。你可以按以下方式向 ArrayList 中添加 Shape形状 ArrayListShape shapes new ArrayList();泛型的应用让 Java 的许多标准库和组件都发生了改变。
对象创建与生命周期
我们在使用对象时要注意的一个关键问题就是对象的创建和销毁方式。每个对象的生存都需要资源尤其是内存。为了资源的重复利用当对象不再被使用时我们应该及时释放资源清理内存。
在简单的编程场景下对象的清理并不是问题。我们创建对象按需使用最后销毁它。然而情况往往要比这更复杂
假设我们正在为机场设计一个空中交通管制的系统该例也适用于仓库货柜管理、影带出租或者宠物寄养仓库系统。第一步比较简单创建一个用来保存飞机的集合每当有飞机进入交通管制区域时我们就创建一个“飞机”对象并将其加入到集合中等到飞机离开时将其从这个集合中清除。与此同时我们还需要一个记录飞机信息的系统也许这些数据不像主要控制功能那样引人注意。比如我们要记录所有飞机中的小型飞机的的信息比如飞行计划。此时我们又创建了第二个集合来记录所有小型飞机。 每当创建一个“飞机”对象的时候将其放入第一个集合若它属于小型飞机也必须同时将其放入第二个集合里。
现在问题开始棘手了我们怎么知道何时该清理这些对象呢当某一个系统处理完成而其他系统可能还没有处理完成。这样的问题在其他的场景下也可能发生。在 C 程序设计中当使用完一个对象后必须明确将其删除这就让问题变复杂了。
对象的数据在哪它的生命周期是怎么被控制的 在 C 设计中采用的观点是效率第一因此它将选择权交给了程序员。为了获得最大的运行时速度程序员可以在编写程序时通过将对象放在栈Stack有时称为自动变量或作用域变量或静态存储区域static storage area中来确定内存占用和生存时间。这些区域的对象会被优先分配内存和释放。这种控制在某些情况下非常有用。
然而相对的我们也牺牲了程序的灵活性。因为在编写代码时我们必须要弄清楚对象的数量、生存时间还有类型。如果我们要用它来解决一个相当普遍的问题时如计算机辅助设计、仓库管理或空中交通管制等限制就太大了。
第二种方法是在堆内存Heap中动态地创建对象。在这种方式下直到程序运行我们才能确定需要创建的对象数量、生存时间和类型。什么时候需要什么时候在堆内存中创建。 因为内存的占用是动态管理的所以在运行时在堆内存上开辟空间所需的时间可能比在栈内存上要长但也不一定。在栈内存开辟和释放空间通常是一条将栈指针向下移动和一条将栈指针向上移动的汇编指令。开辟堆内存空间的时间取决于内存机制的设计。
动态方法有这样一个合理假设对象通常是复杂的相比于对象创建的整体开销寻找和释放内存空间的开销微不足道。原文The dynamic approach makes the generally logical assumption that objects tend to be complicated, so the extra overhead of finding storage and releasing that storage will not have an important impact on the creation of an object.此外更好的灵活性对于问题的解决至关重要。
Java 使用动态内存分配。每次创建对象时使用 new 关键字构建该对象的动态实例。这又带来另一个问题对象的生命周期。较之堆内存在栈内存中创建对象编译器能够确定该对象的生命周期并自动销毁它然而如果你在堆内存创建对象的话编译器是不知道它的生命周期的。在 C 中你必须以编程方式确定何时销毁对象否则可能导致内存泄漏。Java 的内存管理是建立在垃圾收集器上的它能自动发现对象不再被使用并释放内存。垃圾收集器的存在带来了极大的便利它减少了我们之前必须要跟踪的问题和编写相关代码的数量。因此垃圾收集器提供了更高级别的保险以防止潜在的内存泄漏问题这个问题使得许多 C 项目没落。
Java 的垃圾收集器被设计用来解决内存释放的问题虽然这不包括对象清理的其他方面。垃圾收集器知道对象什么时候不再被使用并且自动释放内存。结合单继承和仅可在堆中创建对象的机制Java 的编码过程比用 C 要简单得多。我们所要做的决定和要克服的障碍也会少很多
异常处理
自编程语言被发明以来程序的错误处理一直都是个难题。因为很难设计出一个好的错误处理方案所以许多编程语言都忽略了这个问题把这个问题丢给了程序类库的设计者。他们提出了在许多情况下都可以工作但很容易被规避的半途而废的措施通常只需忽略错误。多数错误处理方案的主要问题是它们依赖程序员之间的约定俗成而不是语言层面的限制。换句话说如果程序员赶时间或没想起来这些方案就很容易被忘记。
异常处理机制将程序错误直接交给编程语言甚至是操作系统。“异常”Exception是一个从出错点“抛出”throw后能被特定类型的异常处理程序捕获(catch)的一个对象。它不会干扰程序的正常运行仅当程序出错的时候才被执行。这让我们的编码更简单不用再反复检查错误了。另外异常不像方法返回的错误值和方法设置用来表示发生错误的标志位那样可以被忽略。异常的发生是不会被忽略的它终究会在某一时刻被处理。
最后“异常机制”提供了一种可靠地从错误状况中恢复的方法使得我们可以编写出更健壮的程序。有时你只要处理好抛出的异常情况并恢复程序的运行即可无需退出。
Java 的异常处理机制在编程语言中脱颖而出。Java 从一开始就内置了异常处理因此你不得不使用它。这是 Java 语言唯一接受的错误报告方法。如果没有编写适当的异常处理代码你将会收到一条编译时错误消息。这种有保障的一致性有时会让程序的错误处理变得更容易。值得注意的是异常处理并不是面向对象的特性。尽管在面向对象的语言中异常通常由对象表示但是在面向对象语言之前也存在异常处理。
其它
面向过程程序包含数据定义和函数调用。要找到程序的意图你必须要在脑中建立一个模型弄清函数调用和更底层的概念。这些程序令人困扰因为它们的表示更多地面向计算机而不是我们要解决的问题这就是我们在设计程序时需要中间表示的原因。OOP 在面向过程编程的基础上增加了许多新的概念所以有人会认为使用 Java 来编程会比同等的面向过程编程要更复杂。在这里我想给大家一个惊喜通常按照 Java 规范编写的程序会比面向过程程序更容易被理解。
你看到的是对象的概念这些概念是站在“问题空间”的而不是站在计算机角度的“解决方案空间”以及发送消息给对象以指示该空间中的活动。面向对象编程的一个优点是设计良好的 Java 程序代码更容易被人阅读理解。由于 Java 类库的复用性通常程序要写的代码也会少得多。
OOP 和 Java 不一定适合每个人。评估自己的需求以及与现有方案作比较是很重要的。请充分考虑后再决定是不是选择 Java。如果在可预见的未来Java 并不能很好的满足你的特定需求那么你应该去寻找其他替代方案Python。如果你依然选择 Java 作为你的开发语言希望你至少应该清楚你选择的是什么以及为什么选择这个方向。