清除时的困境:由谁负责清除?
每个对象都要求资源才能“生存”,其中最令人注目的资源是内存。如果不再需要使用一个对象,就必须将 其清除,以便释放这些资源,以便其他对象使用。如果要解决的是非常简单的问题,如何清除对象这个问题 并不显得很突出:我们创建对象,在需要的时候调用它,然后将其清除或者“破坏”。但在另一方面,我们 平时遇到的问题往往要比这复杂得多。 举个例子来说,假设我们要设计一套系统,用它管理一个机场的空中交通(同样的模型也可能适于管理一个 仓库的货柜、或者一套影带出租系统、或者宠物店的宠物房。这初看似乎十分简单:构造一个集合用来容纳 飞机,然后创建一架新飞机,将其置入集合。对进入空中交通管制区的所有飞机都如此处理。至于清除,在 一架飞机离开这个区域的时候把它简单地删去即可。 但事情并没有这么简单,可能还需要另一套系统来记录与飞机有关的数据。当然,和控制器的主要功能不 同,这些数据的重要性可能一开始并不显露出来。例如,这条记录反映的可能是离开机场的所有小飞机的飞 行计划。所以我们得到了由小飞机组成的另一个集合。一旦创建了一个飞机对象,如果它是一架小飞机,那
36
么也必须把它置入这个集合。然后在系统空闲时期,需对这个集合中的对象进行一些后台处理。 问题现在显得更复杂了:如何才能知道什么时间删除对象呢?用完对象后,系统的其他某些部分可能仍然要 发挥作用。同样的问题也会在其他大量场合出现,而且在程序设计系统中(如C++),在用完一个对象之后 必须明确地将其删除,所以问题会变得异常复杂(注释⑥)。
⑥:注意这一点只对内存堆里创建的对象成立(用 new命令创建的)。但在另一方面,对这儿描述的问题以 及其他所有常见的编程问题来说,都要求对象在内存堆里创建。
在Java 中,垃圾收集器在设计时已考虑到了内存的释放问题(尽管这并不包括清除一个对象涉及到的其他方 面)。垃圾收集器“知道”一个对象在什么时候不再使用,然后会自动释放那个对象占据的内存空间。采用 这种方式,另外加上所有对象都从单个根类Object 继承的事实,而且由于我们只能在内存堆中以一种方式创 建对象,所以Java 的编程要比 C++的编程简单得多。我们只需要作出少量的抉择,即可克服原先存在的大量 障碍。
1. 垃圾收集器对效率及灵活性的影响 既然这是如此好的一种手段,为什么在C++里没有得到充分的发挥呢?我们当然要为这种编程的方便性付出 一定的代价,代价就是运行期的开销。正如早先提到的那样,在C++中,我们可在堆栈中创建对象。在这种 情况下,对象会得以自动清除(但不具有在运行期间随心所欲创建对象的灵活性)。在堆栈中创建对象是为 对象分配存储空间最有效的一种方式,也是释放那些空间最有效的一种方式。在内存堆(Heap)中创建对象 可能要付出昂贵得多的代价。如果总是从同一个基础类继承,并使所有函数调用都具有“同质多形”特征, 那么也不可避免地需要付出一定的代价。但垃圾收集器是一种特殊的问题,因为我们永远不能确定它什么时 候启动或者要花多长的时间。这意味着在 Java 程序执行期间,存在着一种不连贯的因素。所以在某些特殊的 场合,我们必须避免用它——比如在一个程序的执行必须保持稳定、连贯的时候(通常把它们叫作“实时程 序”,尽管并不是所有实时编程问题都要这方面的要求——注释⑦)。
⑦:根据本书一些技术性读者的反馈,有一个现成的实时 Java 系统(www.newmonics.com)确实能够保证垃 圾收集器的效能。
C++语言的设计者曾经向C 程序员发出请求(而且做得非常成功),不要希望在可以使用 C 的任何地方,向语 言里加入可能对C++的速度或使用造成影响的任何特性。这个目的达到了,但代价就是C++的编程不可避免地 复杂起来。Java 比C++简单,但付出的代价是效率以及一定程度的灵活性。但对大多数程序设计问题来说, Java 无疑都应是我们的首选。
1 package Com.TomTest; 2 3 4 class Sum{ 5 int n; 6 float f(){ 7 float sum=0; 8 for(int i=1;i<=n;i++) 9 sum=sum+i; 10 return sum; 11 } 12 } 13 class Average extends Sum { 14 int n; 15 float f(){ 16 float c; 17 super.n=n; 18 c=super.f(); 19 return c/n; 20 } 21 float g(){ 22 float c; 23 c=super.f(); 24 return c/2; 25 } 26 } 27 public class TomTest_30 { 28 public static void main(String args[]) { 29 Average aver=new Average(); 30 aver.n=100; 31 float result_1=aver.f(); 32 float result_2=aver.g(); 33 System.out.println("result_1="+result_1); 34 System.out.println("result_2="+result_2); 35 } 36 }