jimphei學習工作室

          jimphei學習工作室

            BlogJava :: 首頁 :: 新隨筆 :: 聯系 :: 聚合  :: 管理 ::
            23 隨筆 :: 0 文章 :: 1 評論 :: 0 Trackbacks

          11.4.3 對象的finalize()方法簡介

          當垃圾回收器將要釋放無用對象的內存時,先調用該對象的finalize()方法。如果在程序終止之前垃圾回收器始終沒有執行垃圾回收操作,那么垃圾回收器將始終不會調用無用對象的finalize()方法。在Java的Object祖先類中提供了protected類型的finalize()方法,因此任何Java類都可以覆蓋finalize()方法,在這個方法中進行釋放對象所占的相關資源的操作。

          Java虛擬機的垃圾回收操作對程序完全是透明的,因此程序無法預料某個無用對象的finalize()方法何時被調用。另外,除非垃圾回收器認為程序需要額外的內存,否則它不會試圖釋放無用對象占用的內存。換句話說,以下情況是完全可能的:一個程序只占用了少量內存,沒有造成嚴重的內存需求,于是垃圾回收器沒有釋放那些無用對象占用的內存,因此這些對象的finalize()方法還沒有被調用,程序就終止了。

          程序即使顯式調用System.gc()或Runtime.gc()方法,也不能保證垃圾回收操作一定執行,因此不能保證無用對象的finalize()方法一定被調用。

          11.4.4 對象的finalize()方法的特點

          對象的finalize()方法具有以下特點:

          垃圾回收器是否會執行該方法及何時執行該方法,都是不確定的。

          finalize()方法有可能使對象復活,使它恢復到可觸及狀態。

           垃圾回收器在執行finalize()方法時,如果出現異常,垃圾回收器不會報告異常,程序繼續正常運行。

          下面結合一個具體的例子來解釋finalize()方法的特點。例程11-13的Ghost類是一個帶實例緩存的不可變類,它的finalize()方法能夠把當前實例重新加入到實例緩存ghosts中。

          例程11-13 Ghost.java

          import java.util.Map;

          import java.util.HashMap;

          public class Ghost {

          private static final Map<String,Ghost> ghosts=new HashMap<String,Ghost>();

          private final String name;

          public Ghost(String name) {

          this.name=name;

          }

          public String getName(){return name;}

          public static Ghost getInstance(String name){

          Ghost ghost =ghosts.get(name);

          if (ghost == null) {

          ghost=new Ghost(name);

          ghosts.put(name,ghost);

          }

          return ghost;

          }

          public static void removeInstance(String name){

          ghosts.remove(name);

          }

          protected void finalize()throws Throwable{

          ghosts.put(name,this);

          System.out.println("execute finalize");

          //throw new Exception("Just Test");

          }

          public static void main(String args[])throws Exception{

          Ghost ghost=Ghost.getInstance("IAmBack"); //①

          System.out.println(ghost); //②

          String name=ghost.getName(); //③

          ghost=null; //④

          Ghost.removeInstance(name); //⑤

          System.gc(); //⑥

          //把CPU讓給垃圾回收線程

          Thread.sleep(3000); //⑦

          ghost=Ghost.getInstance("IAmBack"); //⑧

          System.out.println(ghost); //⑨

          }

          }

          運行以上Ghost類的main()方法,一種可能的打印結果為:

          Ghost@3179c3

          execute finalize

          Ghost@3179c3

          以上程序創建了3個對象:1個Ghost對象、1個常量字符串“IAmBack”及1個HashMap對象。當程序執行完main()方法的第③行時,內存中引用變量與對象之間的關系如圖11-9所示。

          圖11-9 Ghost對象與其他對象及引用變量的關系

          當執行完第④行時,ghost變量被置為null,此時Ghost對象依然被ghosts屬性間接引用,因此仍然處于可觸及狀態。當執行完第⑤行時,Ghost對象的引用從HashMap對象中刪除,Ghost對象不再被程序引用,此時進入可復活狀態,即變為無用對象。

          第⑥行調用System.gc()方法,它能提高垃圾回收器盡快執行垃圾回收操作的可能性。假如垃圾回收器線程此刻獲得了對CPU的使用權,它將調用Ghost對象的finalize()方法。該方法把Ghost對象的引用又加入到HashMap對象中,Ghost對象又回到可觸及狀態,垃圾回收器放棄回收它的內存。執行完第⑧行,ghost變量又引用這個Ghost對象。

          假如對finalize()做一些修改,使它拋出一個異常:

          protected void finalize()throws Throwable{

          ghosts.put(name,this);

          System.out.println("execute finalize");

          throw new Exception("Just Test");

          }

          程序的打印結果不變。由此可見,當垃圾回收器執行finalize()方法時,如果出現異常,垃圾回收器不會報告異常,也不會導致程序異常中斷。

          假如在程序運行中,垃圾回收器始終沒有執行垃圾回收操作,那么Ghost對象的finalize()方法就不會被調用。讀者不妨把第⑥行的System.gc()和第⑦行的Thread.sleep(3000)方法注釋掉,這樣更加可能導致finalize()方法不會被調用,此時程序的一種可能的打印結果為:

          Ghost@3179c3

          Ghost@310d42

          從以上打印結果可以看出,由于Ghost對象的finalize()方法沒有被執行,因此這個Ghost對象在程序運行期間始終沒有復活。當程序第二次調用Ghost.getInstance("IAmBack")方法時,該方法創建了一個新的Ghost對象。

          值得注意的是,以上例子僅僅用于演示finalize()方法的特性,在實際應用中,不提倡用finalize()方法來復活對象。可以把處于可觸及狀態的對象比做活在陽間的人,把不處于這個狀態的對象(無用對象)比做到了陰間的人。程序所能看見和使用的是陽間的人,假如閻王經常悄悄地讓幾個陰間的人復活,使他們在程序毫不知情的情況下溜回陽間,這只會擾亂程序的正常執行流程。

          11.4.5 比較finalize()方法和finally代碼塊

          在Object類中提供了finalize()方法,它的初衷是用于在對象被垃圾回收器回收之前,釋放所占用的相關資源,這和try…catch…finally語句的finally代碼塊的用途比較相似。但由于垃圾回收器是否會執行finalize()方法及何時執行該方法,都是不確定的,因此在程序中不能用finalize()方法來完成同時具有以下兩個特點的釋放資源的操作。

           必須執行。

          必須在某個確定的時刻執行。

          具有以上特點的操作更適合于放在finally代碼塊中。此外,可以在類中專門提供一個用于釋放資源的公共方法,最典型的就是java.io.InputStream和java.io.OutputStream類的close()方法,它們用于關閉輸入流或輸出流。當程序中使用了一個輸入流時,在結束使用前應該確保關閉輸入流。

          InputStream in;

          try{

          InputStream in=new FileInputStream("a.txt");

          }catch(IOException e){

          }finally{

          try{in.close();}catch(IOException e){…}

          }

          在多數情況下,應該避免使用finalize()方法,因為它會導致程序運行結果的不確定性。在某些情況下,finalize()方法可用來充當第二層安全保護網,當用戶忘記顯式釋放相關資源時,finalize()方法可以完成這一收尾工作。盡管finalize()方法不一定會被執行,但是有可能會釋放資源,這總比永遠不會釋放資源更安全。

          可以用自動洗衣機的關機功能來解釋finalize()方法的用途。自動洗衣機向用戶提供了專門的關機按鈕,這相當于AutoWasher類的close()方法,假如用戶忘記關機,相當于忘記調用AutoWasher對象的close()方法,那么自動洗衣機會在洗衣機停止工作后的1個小時內自動關機,這相當于調用finalize()方法。當然,這個例子不是太貼切,因為如果用戶忘記關機,洗衣機的自動關機操作總會被執行。

          posted on 2009-06-08 17:08 jimphei 閱讀(151) 評論(0)  編輯  收藏

          只有注冊用戶登錄后才能發表評論。


          網站導航:
           
          主站蜘蛛池模板: 扶风县| 丰县| 东至县| 广丰县| 肇源县| 巴塘县| 宁夏| 扎鲁特旗| 汶上县| 淮阳县| 敖汉旗| 克什克腾旗| 泰和县| 靖江市| 静安区| 出国| 渭南市| 闽清县| 镇沅| 安阳县| 竹北市| 资兴市| 阿勒泰市| 永善县| 浦城县| 巫溪县| 江源县| 固镇县| 镇远县| 洛南县| 叶城县| 五常市| 定远县| 玉环县| 松江区| 法库县| 无极县| 台中市| 舟曲县| 渝北区| 祁阳县|