熱點推薦:
您现在的位置: 電腦知識網 >> 編程 >> Java編程 >> Java核心技術 >> 正文

Java中的finalize詳解

2022-06-13   來源: Java核心技術 

  程序員都了解初始化的重要性但常常會忘記同樣重要的清除工作畢竟誰需要清除一個int 呢?但在使用程序庫時把一個對象用完後就棄之不顧的做法並非總是安全的當然Java有垃圾回收器來回收無用對象占據的內存資源但也有特殊情況假定你的對象(並非使用 new)獲得了一塊特殊的內存區域由於垃圾回收器只知道釋放那些經由 new分配的內存所以它不知道該如何釋放該對象的這塊 特殊內存為了應對這種情況Java允許你在類中定義一個名為finalize( )的方法它的工作原理應該是這樣的一旦垃圾回收器准備好釋放對象占用的存儲空間將首先調用其 finalize( )方法並且在下一次垃圾回收動作發生時才會真正回收對象占用的內存所以要是你打算用 finalize( )就能在垃圾回收時刻做一些重要的清除工作

  這裡有一個潛在的編程陷阱因為有些程序員(特別是 C++程序員)剛開始可能會誤把finalize( )當作C++中的析構函數(C++中銷毀對象必須用到這個函數)所以有必要明確區分一下在 C++中對象一定會被銷毀(如果程序中沒有錯誤的話)而 Java 裡的對象卻並非總是被垃圾回收或者換句話說

  . 對象可能不被回收

  . 垃圾回收並不等於析構

  牢記這些你就能遠離困擾這意味著在你不再需要某個對象之前如果必須執行某些動作那麼你得自己去做Java並未提供析構函數或相似的概念要做類似的清除工作你必須自己動手創建一個執行清除工作的普通方法例如假設某個對象在創建過程中會將自己繪制到屏幕上要是你不明確地從屏幕上將其擦除它可能永遠得不到清除如果在

  finalize( )裡加入某種擦除功能垃圾回收發生時(不能保證一定會發生)finalize( )得到了調用圖像就會被擦除要是垃圾回收沒有發生圖像就會一直保留下來

  也許你會發現只要程序沒有瀕臨存儲空間用完的那一刻對象占用的空間就總也得不到釋放如果程序執行結束並且垃圾回收器一直都沒有釋放你創建的任何對象的存儲空間則隨著程序的退出那些資源會全部交還給操作系統這個策略是恰當的因為垃圾回收本身也有開銷要是不使用它那就不用支付這部分開銷了

  finalize( )用途何在?

  此時你已經明白了不該將finalize( )作為通用的清除方法那麼finalize( )的真正用途是什麼呢?

  這引出了要記住的第三點

  .垃圾回收只與內存有關

  也就是說垃圾回收器存在的唯一原因是為了回收程序不再使用的內存所以對於與垃圾回收有關的任何行為來說(尤其是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)

  Systemoutprintln(Error: checked out);

  // Normally youll also do this:

  // superfinalized();

  }

  }

  public class TerminationCondition {

  public static void main(String[] args) {

  Book novel = new Book(true);

  // Proper cleanup:

  novelcheckIn();

  // Drop the reference forget to clean up:

  new Book(true);

  // Force garbage collection & finalization:

  Systemgc();

  }

  }

  本例的終結條件是所有的Book對象在被當作垃圾回收前都應該被簽入(check in)但在main( )方法中由於程序員的錯誤有一本書未被簽入要是沒有 finalize( )來驗證終結條件將很難發現這種錯誤

  注意Systemgc( )用於強制終結動作的進行即使不這麼做的話通過重復的執行程序(假設程序將分配大量的存儲空間而導致垃圾回收動作的執行)最終也能找出錯誤的Book對象


From:http://tw.wingwit.com/Article/program/Java/hx/201311/26018.html
    推薦文章
    Copyright © 2005-2022 電腦知識網 Computer Knowledge   All rights reserved.