finalize方法使用.docx_第1页
finalize方法使用.docx_第2页
finalize方法使用.docx_第3页
finalize方法使用.docx_第4页
finalize方法使用.docx_第5页
全文预览已结束

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

程序员都了解初始化的重要性,但常常会忘记同样重要的清除工作。毕竟,谁需要清除一个int 呢?但在使用程序库时,把一个对象用完后就“弃之不顾”的做法并非总是安全的。当然,Java有垃圾回收器来回收无用对象占据的内存资源。但也有特殊情况:假定你的对象(并非使用 new)获得了一块“特殊”的内存区域,由于垃圾回收器只知道释放那些经由 new分配的内存,所以它不知道该如何释放该对象的这块 “特殊”内存。为了应对这种情况,Java允许你在类中定义一个名为finalize( )的方法。它的工作原理“应该”是这样的:一旦垃圾回收器准备好释放对象占用的存储空间,将首先调用其 finalize( )方法,并且在下一次垃圾回收动作发生时,才会真正回收对象占用的内存。所以要是你打算用 finalize( ),就能在“垃圾回收时刻”做一些重要的清除工作。这里有一个潜在的编程陷阱,因为有些程序员(特别是 C+程序员)刚开始可能会误把finalize( )当作C+中的“析构函数”(C+中销毁对象必须用到这个函数)。所以有必要明确区分一下:在 C+中,对象一定会被“销毁”(如果程序中没有错误的话);而 Java 里的对象却并非总是被“垃圾回收”的。或者换句话说:1 对象可能不被回收。2 垃圾回收并不等于“析构”。牢记这些,你就能远离困扰。这意味着在你不再需要某个对象之前,如果必须执行某些动作,那么你得自己去做。Java并未提供“析构函数”或相似的概念,要做类似的清除工作,你必须自己动手创建一个执行清除工作的普通方法。例如,假设某个对象在创建过程中,会将自己绘制到屏幕上。要是你不明确地从屏幕上将其擦除,它可能永远得不到清除。如果在finalize( )里加入某种擦除功能,当“垃圾回收”发生时(不能保证一定会发生),finalize( )得到了调用,图像就会被擦除。要是“垃圾回收”没有发生,图像就会一直保留下来。也许你会发现,只要程序没有濒临存储空间用完的那一刻,对象占用的空间就总也得不到释放。如果程序执行结束,并且垃圾回收器一直都没有释放你创建的任何对象的存储空间,则随着程序的退出,那些资源会全部交还给操作系统。这个策略是恰当的,因为垃圾回收本身也有开销,要是不使用它,那就不用支付这部分开销了。finalize( )用途何在?此时,你已经明白了不该将finalize( )作为通用的清除方法。那么,finalize( )的真正用途是什么呢?这引出了要记住的第三点:3垃圾回收只与内存有关。也就是说,垃圾回收器存在的唯一原因是为了回收程序不再使用的内存。所以对于与垃圾回收有关的任何行为来说(尤其是finalize( )方法),它们也必须同内存及其回收有关。但这是否意味着要是对象中含有其他对象,finalize( )就应该明确释放那些对象呢?不无论对象是如何创建的,垃圾回收器都会负责释放对象占据的所有内存。这就将对 finalize( )的需求限制到特殊情况之下:你通过某种非“创建对象”的方式为对象分配了存储空间。不过,你也看到了,Java中一切皆为对象,那这种特殊情况是怎么回事呢?看来之所以要有finalize( ),是由于你可能在分配内存时,采用了类似C语言中的做法而非Java中的通常做法。这种情况主要发生在使用“本地方法”的情况下,它是在Java中调用非Java代码的一种方式。本地方法目前只支持C和C+。但它们可以调用其它语言写的代码,所以你实际上可以调用任何代码。在非Java代码中,也许会调用类似C的malloc( )函数,用它分配存储空间,而且除非调用了free( )函数,否则存储空间将不会得到释放,从而造成内存泄露。当然,free( )是C和C+中的函数,所以你需要在finalize( )中用本地方法调用它。至此,你或许已经明白了不要过多地使用finalize( )的道理了。对,它确实不是进行普通的清除工作的合适场所。那么,普通的清除工作应该在哪执行呢?你必须执行清除为清除一个对象,用户必须在进行清除的时刻调用执行清除动作的方法。听起来似乎很简单,但却与C+中的“析构函数”的概念稍有抵触。在C+中,所有对象都会被销毁,或者说, “应该”被销毁。如果在C+中创建了一个局部对象(就是在堆栈上创建,Java中可不行),此时的销毁动作发生在以“右花括号”为边界的、此对象作用域的末尾处进行。如果对象是用new创建的(类似于Java),那么当程序员调用C+的delete( )时(Java没有这个命令),就会调用相应的析构函数。如果程序员忘了,那么永远不会调用析构函数,就会出现内存泄露,对象的其他部分也不会得到清除。这种错误很难跟踪,这也是让 C+程序员转向 Java的一个主要因素。相反,Java不允许创建局部对象,你必须使用new。在Java中,也没有“delete”来释放对象,因为垃圾回收器会帮助你释放存储空间。甚至可以肤浅地认为,正是由于垃圾收集机制的存在,使得 Java 没有析构函数。然而,随着学习的深入,你就会明白垃圾回收器的存在并不能完全代替析构函数。(而且你绝对不能直接调用finalize( ),所以这也不是一个恰当的途径。)如果你希望进行除释放存储空间之外的清除工作,你还是得明确调用某个恰当的Java方法。这就等同于使用析构函数了,而且没有它方便。记住,无论是“垃圾回收”还是“终结”,都不保证一定会发生。如果Java虚拟机(JVM)并未面临内存耗尽的情形,它是不会浪费时间在回收垃圾以恢复内存上的。终结条件通常,你不能指望finalize( ),你必须创建其它的“清除”方法,并且明确地调用它们。看来,finalize( )只能存在于程序员很难用到的一些晦涩用法里了。不过,finalize( )还有一个有趣的用法,它并不依赖于每次都要对finalize( )进行调用,这就是对象“终结条件”的验证。当你对某个对象不再感兴趣,也就是它可以被清除时,这个对象应该处于某种状态,使它占用的内存可以被安全地释放。例如,要是对象代表了一个打开的文件,在对象被回收前程序员应该关闭这个文件。只要对象中存在没有被适当清除的部分,你的程序就存在很隐晦的错误。finalize( )的价值在于可以用来最终发现这种情况,尽管它并不总是会被调用。如果某次finalize( )的动作使得bug被发现,那你就可据此找出问题所在这才是你真正关心的。以下是个简单的例子,示范了可能的使用方式:class Book boolean checkedOut = false;Book(boolean checkOut) checkedOut = checkOut;void checkIn() checkedOut = false;public void finalize() if(checkedOut)System.out.println(Error: checked out);/ Normally, youll also do this:/ super.finalized();public class TerminationCondition public static void main(String args) Book novel = new Book(true);/ Proper cleanup:novel.checkIn();/ Drop the reference, forget to clean up:new Book(true);/ Force garbage collection & finalization:System.gc();本例的终结条件是:所有的Book对象在被当作垃圾回收前都应该被签入(check in)。但在main( )方法中,由于程序员的错误,有一本书未被签入。要是没有 finalize( )来验证终结条件,将很难发现这种错误。注意,System.gc( )用于强制终结动作的进行。即使不这么做的话,通过重复的执行程序(假设程序将分配大量的存储空间而导致垃圾回收动作的执行),最终也能找出错误的Book对象。1垃圾收集器的工作目标是回收已经无用的对象的内存空间,从而避免内存渗漏体的产生,节省内存资源,避免程序代码的崩溃。2垃圾收集器判断一个对象的内存空间是否无用的标准是:如果该对象不能再被程序中任何一个活动的部分所引用,此时我们就说,该对象的内存空间已经无用。所谓活动的部分,是指程序中某部分参与程序的调用,正在执行过程中,尚未执行完毕。3垃圾收集器线程虽然是作为低优先级的线程运行,但在系统可用内存量过低的时候,它可能会突发地执行来挽救内存资源。当然其执行与否也是不可预知的。4垃圾收集器不可以被强制执行,但程序员可以通过调用System. gc方法来建议执行垃圾收集器。 5不能保证一个无用的对象一定会被垃圾收集器收集,也不能保证垃圾收集器在一段Java语言代码中一定会执行。因此在程序执行过程中被分配出去的内存空间可能会一直保留到该程序执行完毕,除非该空间被重新分配或被其他方法回收。由此可见,完全彻底地根绝内存渗漏体的产生也是不可能的。但是请不要忘记,Java的垃圾收集器毕竟使程序员从手工回收内存空间的繁重工作中解脱了出来。设想一个程序员要用C或C+来编写一段10万行语句的代码,那么他一定会充分体会到Java的垃圾收集器的优点!6同样没有办法预知在一组均符合垃圾收集器收集标准的对象中,哪一个会被首先收集。7循环引用对象不会影响其被垃圾收集器收集。8可以通过将对象的引用变量(reference variables,即句柄handles)初始化为null值,来暗示垃圾收集器来收集该对象。但此时,如果该对象连接有事件监听器(典型的 AWT组件),那它还是不可以被收集。所以在设一个引用变量为null值之前,应注意该引用变量指向的对象是否被监听,若有,要首先除去监听器,然后才可以赋空值。9每一个对象都有一个finalize( )方法,这个方法是从Object类继承来的。10finalize( )方法用来回收内存以外的系统资源,就像是文件处理器和网络连接器。该方法的调用顺序和用来调用该方法的对象的创建顺序是无关的。换句话说,书写程序时该方法的顺序和方法的实际调用顺序是不相干的。请注意这只是finalize( )方法的特点。11每个对象只能调用finalize( )方法一次。如果在finalize( )方法执行时产生异常(exception),则该对象仍可以被垃圾收集器收集。12垃圾收集器跟踪每一个对象,收集那些不可到达的对象(即该对象没有被程序的任何活的部分所调用),回收其占有的内存空间。但在进行垃圾收集的时候,垃圾收集器会调用finalize( )方法,通过让其他对象知道它的存在,而使不可到达的对象再次复苏为可到达的对象。既然每个对象只能调用一次finalize( )方法,所以每个对象也只可能复苏一次。13finalize( )方法可以明确地被调用,但它却不能进行垃圾收集。14finalize( )方法可以被重载(overload),但只有具备初始的finalize( )方法特点的方法才可以被垃圾收集器调用。15子类的finalize( )方法可以明确地调用父类的finalize( )方法,作为该子类对象的最后一次适当的操作。但Java编译器却不认为这是一次覆盖操作(overriding),所以也不会对其调用进行检查。16当finalize( )方法尚未被调用时,System. runFinalization( )方法可以用来调用finalize( )方法,并实现相同的效果,对无用对象进行垃圾收集。17当一个方法执行完毕,其中的局部变量就会超出使用范围,此时可以被当作垃圾收集,但以后每当该方法再次被调用时,其中的局部变量便会被重新创建。18Java语言使用了一种标记交换区的垃圾收集算法。该算法会遍历程序中每

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论