005第1章 对象导论1.10对象的创建和生命期、1.11异常处理:处理错误、1.12并发编程

1.10 对象的创建和生命期
在使用对象时,最关键的问题之一便是它们的生成和销毁方式。
每个对象为了生存都需要资源,尤其是内存。
当我们不再需要一个对象时,它必须被清理掉,使其占有的资源可以被释放和重用。

空中交通管理系统、
怎样才能知道何时销毁这些对象呢?
当处理完某个对象之后,系统某个其他部分可能还在处理它。
在其他许多场合中也会遇到同样的问题,在必须明确删除对象的编程系统中(例如C++),此问题会变得十分复杂。
对象的数据位于何处?怎样控制对象的生命周期?
C++认为效率控制是最重要的议题,所以给程序员提供了选择的权力。牺牲了灵活性
第二种方式是在被称为堆(heap)的内存池中动态地创建对象。
在这种方式中,直到运行时才知道需要多少对象,它们的生命周期如何,以及它们的具体类型是什么。
这些问题的答案只能在程序运行时相关代码被执行到的那一刻才能确定。
动态方式有这样一个一般性的逻辑假设:对象趋向于变得复杂,所以查找和释放存储空间的开销不会对对象的创建造成重大冲击。
动态方式所带来的更大的灵活性正是解决一般化编程问题的要点所在。

Java完全采用了动态内存分配方式。
每当想要创建新对象时,就要使用new关键字来构建此对象的动态实例。
还有一个议题?就是对象生命周期。对于允许在堆栈上创建对象的语言,编译器可以确定对象存活的时间,并可以自动销毁它。
在像C++这样的语言中,必须通过编程方式来确定何时销毁对象,这可能会因为不能正确处理而导致内存泄漏(这在C++程序中是常见的问题)
Java提供了被称为“垃圾回收器”的机制,它可以自动发现对象何时不再被使用,并继而销毁它。
垃圾回收器非常有用,因为它减少了所必须考虑的议题和必须编写的代码口,
更重要的是,垃圾回收器提供了更高层的保障,可以避免暗藏的内存泄漏问题,这个问题已经使许多C++项目折戟沉沙。
Java的垃圾回收器被设计用来处理内存释放问题(尽管它不包括清理对象的其他方面)。
垃圾回收器“知道”对象何时不再被使用,并自动释放对象占用的内存。
这一点同所有对象都是继承自单根基类Object以及只能以一种方式创建对象(在堆上创建)这两个特性结合起来,
使得用Java编程的过程较之用C++编程要简单得多,所要做出的决策和要克服的障碍也要少得多。

1.11 异常处理:处理错误
自从编程语言问世以来,错误处理就始终是最困难的问题之一。
因为设计一个良好的错误处理机制非常困难
异常处理将错误处理直接置于编程语言中,有时甚至置于操作系统中。
异常是一种对象,它从出错地点被“抛出”,并被专门设计用来处理特定类型错误的相应的异常处理器“捕获”。
异常处理就像是与程序正常执行路径并行的,在错误发生时执行的另一条路径。
因为它是另一条完全分离的执行路径,所以它不会干扰正常的执行代码。

这往往使得代码编写变得简单,因为不需要被迫定期检查错误。
此外,被抛出的异常不像方法返回的错误值和方法设置的用来表示错误条件的标志位那样可以被忽略。
异常不能被忽略,所以它保证一定会在某处得到处理。
最后需要指出的是:异常提供了一种从错误状况进行可靠恢复的途径。
现在不再是只能退出程序,你可以经常进行校正,并恢复程序的执行,这些都有助于编写出更健壮的程序。

Java的异常处理在众多的编程语言中格外引人注目,因为Java一开始就内置了异常处理,而且强制你必须使用它。
它是唯一可接受的错误报告方式。
如果没有编写正确的处理异常的代码,那么就会得到一条编译时的出错消息。
这种有保障的一致性有时会使得错误处理非常容易。

值得注意的是,异常处理不是面向对象的特征——尽管在面向对象语言中异常常被表示成为一个对象。
异常处理在面向对象语言出现之前就已经存在了。

1.12 并发编程
在计算机编程中有一个基本概念,就是在同一时刻处理多个任务的思想。
许多程序设计问题都要求,程序能够停下正在做的工作,转而处理某个其他问题,然后再返回主进程。
最初,主进程的挂起是通过硬件中断来触发的。
但是对于大量的其他问题于我们只是想把问题切分成多个可独立运行的部分(任务),从而提高程序的响应能力。
在程序中,这些彼此独立运行的部分称之为线程,上述概念被称为”并发”。
并发最常见的例子就是用户界面。通过使用任务,用户可以在揿[qìn]下按钮后快速得到一个响应,而不用被迫等待到程序完成当前任务为止。
通常,线程只是一种为单一处理器分配执行时间的手段。
所有这些都使得并发看起来相当简单,但是有一个隐患:共享资源。
如果有多个并行任务都要访问同一项资源,那么就会出问题。
例如,两个进程不能同时向一台打印机发送信息。
为了解决这个问题,可以共享的资源,例如打印机,必须在使用期间被锁定
因此,整个过程是:某个任务锁定某项资源令完成其任务,然后释放资源锁,使其他任务可以使用这项资源。

Java的并发是内置于语言中的,Java SE5已经增添了大量额外的库支持。


以下为原书原文

1.10 对象的创建和生命期

在使用对象时,最关键的问题之一便是它们的生成和销毁方式。
每个对象为了生存都需要资源,尤其是内存。
当我们不再需要一个对象时,它必须被清理掉,使其占有的资源可以被释放和重用。
在相对简单的编程情况下,怎样清理对象看起来似乎不是什么挑战:你创建了对象,根据需要使用它,然后它应该被销毁。
然而,你很可能会遇到相对复杂的情况。

例如,假设你正在为某个机场设计空中交通管理系统(同样的模型在仓库货柜管理系统、录像带出租系统或宠物寄宿店也适用)。
一开始问题似乎很简单:创建一个容器来保存所有的飞机,然后为每一架进入空中交通控制区域的飞机创建一个新的飞机对象,并将其置于容器中。
对于清理工作,只需在飞机离开此区域时删除相关的飞机对象即可。

但是,可能还有别的系统记录若有关飞机的数据,也许这些数据不需要像主要控制功能那样立即引人注意。
例如,它可能记录着所有飞离机场的小型飞机的飞行计划。
因此你需要有第二个容器来存放小型飞机,无论何时,只要创建的是小型飞机对象,那么它同时也应该置入第二个容器内。
然后某个后台进程在空闲时对第二个容器内的对象进行操作。

现在问题变得更困难了:怎样才能知道何时销毁这些对象呢?
当处理完某个对象之后,系统某个其他部分可能还在处理它。
在其他许多场合中也会遇到同样的问题,在必须明确删除对象的编程系统中(例如C++),此问题会变得十分复杂。

对象的数据位于何处?怎样控制对象的生命周期?
C++认为效率控制是最重要的议题,所以给程序员提供了选择的权力。
为了追求最大的执行速度,对象的存储空间和生命周期可以在编写程序时确定,这可以通过将对象置于堆栈
(它们有时被称为自动变量(automatic variable)限域变址(scoped variable))或静态存储区域内来实现。
这种方式将存储空间分配和释放置于优先考虑的位置,某些情况下这样控制非常有价值。
但是,也牺牲了灵活性,因为必须在编写程序时知道对象确切的数量、生命周期和类型。
如果试图解决更一般化的问题,例如计算机辅助设计、仓库管理或者空中交通控制,这种方式就显得过于受限了。

第二种方式是在被称为堆(heap)的内存池中动态地创建对象。
在这种方式中,直到【运行时】才知道需要多少对象,它们的生命周期如何,以及它们的具体类型是什么。
这些问题的答案只能在程序运行时相关代码被执行到的那一刻才能确定。
如果需要一个新对象,可以在需要的时刻直接在堆中创建。
因为存储空间是在运行时被动态管理的,所以需要大量的时间在堆中分配存储空间,这可能要远远大于在堆栈中创建存储空间的时间
在堆栈中创建存储空间和释放存储空间通常各需要一条汇编指令即可,分别对应将栈顶指针向下移动和将栈顶指针向上移动。
创建堆存储空间的时间依赖于存储机制的设计。

动态方式有这样一个一般性的逻辑假设:对象趋向于变得复杂,所以查找和释放存储空间的开销不会对对象的创建造成重大冲击。
动态方式所带来的更大的灵活性正是解决一般化编程问题的要点所在。

Java完全采用了动态内存分配方式。
每当想要创建新对象时,就要使用new关键字来构建此对象的动态实例。

还有一个议题?就是对象生命周期。对于允许在堆栈上创建对象的语言,编译器可以确定对象存活的时间,并可以自动销毁它。
然而,如果是在堆上创建对象,编译器就会对它的生命周期一无所知。
在像C++这样的语言中,必须通过编程方式来确定何时销毁对象,这可能会因为不能正确处理而导致内存泄漏(这在C++程序中是常见的问题)
Java提供了被称为“垃圾回收器”的机制,它可以自动发现对象何时不再被使用,并继而销毁它。
垃圾回收器非常有用,因为它减少了所必须考虑的议题和必须编写的代码口,
更重要的是,垃圾回收器提供了更高层的保障,可以避免暗藏的内存泄漏问题,这个问题已经使许多C++项目折戟沉沙。

Java的垃圾回收器被设计用来处理内存释放问题(尽管它不包括清理对象的其他方面)。
垃圾回收器“知道”对象何时不再被使用,并自动释放对象占用的内存。
这一点同所有对象都是继承自单根基类Object以及只能以一种方式创建对象(在堆上创建)这两个特性结合起来,
使得用Java编程的过程较之用C++编程要简单得多,所要做出的决策和要克服的障碍也要少得多。

1.11 异常处理:处理错误

自从编程语言问世以来,错误处理就始终是最困难的问题之一。
因为设计一个良好的错误处理机制非常困难
,所以许多语言直接略去这个问题,将其交给程序库设计者处理,
而这些设计者也只是提出了一些不彻底的方法,这些方法可用于许多很容易就可以绕过此问题的场合,而且其解决方式通常也只是忽略此问题。
大多数错误处理机制的主要问题在于,它们都依赖于程序员自身的警惕性,这种警惕性来源于一种共同的约定于而不是编程语言所强制的。
如果程序员不够警惕——通常是因为他们太忙,这些机制就很容易被忽视。

异常处理将错误处理直接置于编程语言中,有时甚至置于操作系统中。
异常是一种对象,它从出错地点被“抛出”,并被专门设计用来处理特定类型错误的相应的异常处理器“捕获”。
异常处理就像是与程序正常执行路径并行的,在错误发生时执行的另一条路径。
因为它是另一条完全分离的执行路径,所以它不会干扰正常的执行代码。
这往往使得代码编写变得简单,因为不需要被迫定期检查错误。
此外,被抛出的异常不像方法返回的错误值和方法设置的用来表示错误条件的标志位那样可以被忽略。
异常不能被忽略,所以它保证一定会在某处得到处理。
最后需要指出的是:异常提供了一种从错误状况进行可靠恢复的途径。
现在不再是只能退出程序,你可以经常进行校正,并恢复程序的执行,这些都有助于编写出更健壮的程序。

Java的异常处理在众多的编程语言中格外引人注目,因为Java一开始就内置了异常处理,而且强制你必须使用它。
它是唯一可接受的错误报告方式。

如果没有编写正确的处理异常的代码,那么就会得到一条编译时的出错消息。
这种有保障的一致性有时会使得错误处理非常容易。

值得注意的是,异常处理不是面向对象的特征——尽管在面向对象语言中异常常被表示成为一个对象。
异常处理在面向对象语言出现之前就已经存在了。

1.12 并发编程

在计算机编程中有一个基本概念,就是在同一时刻处理多个任务的思想。
许多程序设计问题都要求,程序能够停下正在做的工作,转而处理某个其他问题,然后再返回主进程。
有许多方法可以实现这个目的。
最初,程序员们用所掌握的有关机器底层的知识来编写中断服务程序,主进程的挂起是通过硬件中断来触发的。
尽管这么做可以解决问题,但是其难度太大,而且不能移植,所以使得将程序移植到新型号的机器上时,既费时又费力。

有时中断对于处理时间性强的任务是必需的,但是对于大量的其他问题于我们只是想把问题切分成多个可独立运行的部分(任务),从而提高程序的响应能力。
在程序中,这些彼此独立运行的部分称之为线程,上述概念被称为”并发”。
并发最常见的例子就是用户界面。通过使用任务,用户可以在揿[qìn]下按钮后快速得到一个响应,而不用被迫等待到程序完成当前任务为止。

通常,线程只是一种为单一处理器分配执行时间的手段。
但是如果操作系统支持多处理器,那么每个任务都可以被指派给不同的处理器,并且它们是在真正地并行执行。
在语言级别上,多线程所带来的便利之一便是程序员不用再操心机器上是有多个处理器还是只有一个处理器。
由于程序在逻辑上被分为线程,所以如果机器拥有多个处理器,那么程序不需要特殊调整也能执行得更快。

所有这些都使得并发看起来相当简单,但是有一个隐患:共享资源。
如果有多个并行任务都要访问同一项资源,那么就会出问题。
例如,两个进程不能同时向一台打印机发送信息。
为了解决这个问题,可以共享的资源,例如打印机,必须在使用期间被锁定。
因此,整个过程是:某个任务锁定某项资源令完成其任务,然后释放资源锁,使其他任务可以使用这项资源。

Java的并发是内置于语言中的,Java SE5已经增添了大量额外的库支持。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值