2015-09-16 28 views
3

我有代碼(簡化)這樣的:如何使用嵌套對象層次結構來確保資源被釋放?

class A { 

    B b = new B(); 

    void close() { 
     b.close(); 
    } 
} 

class B { 

    Closeable mustBeClosed = new Closeable() { 
     { 
      System.out.println("create"); 
     } 
     @Override 
     public void close() { 
      System.out.println("close"); 
     } 
    }; 

    int n = 0/0; 

    void close() { 
     mustBeClosed.close(); 
    } 
} 

//code 
try (A a = new A()) { 
    //do something 
} 

如何保證mustBeClosed被釋放?

當對象層次結構複雜時,可能會發生這種情況。重寫最終確定B可能不是一個完美的解決方案。

針對這個問題的最佳實踐或原則?

修訂版的樣子:

class B { 
    Closeable mustBeClosed; 
    B() { 
     try { 

      mustBeClosed = ... 

      //other initialization which might raise exceptions 

     } catch (throwable t) { 
      close(); 
      throw t; 
     } 
    } 

    void close() { 
     if (mustBeClosed != null) { 
      try { 
       mustBeClosed.close(); 
      } catch (Throwable t) { 
      } 
     } 
     //all other resources that should be closed 
    } 
} 

然而,這需要太多的代碼是遠離優雅。更重要的是,所有權層次結構中的所有類似乎都應遵循相同的樣式,這會產生大量代碼。

有什麼建議嗎?

回答

1

你的問題是,如果構造函數拋出一個異常,try-with-resources不會(實際上不能)調用close()

任何對象構造,其分配資源,並具有後分配資源建設過程中失敗的可能性,異常級聯起來調用堆棧之前必須釋放資源。

兩種方式來解決這個問題:

1)請確保資源分配是最後完成的動作。在你的情況下,這意味着將字段n上移到字段mustBeClosed之前。

2)在構造函數中處理資源構造,而不是在字段初始值設定項中,這樣您可以捕獲任何後續異常,並在重新拋出異常之前再次釋放該資源,如替代​​解決方案所示。
但是,您不必在close()方法中進行空檢查,因爲如果對象構造成功,則mustBeClosed將始終爲非空,並且如果對象構造失敗,則不會調用close()

0

使用封裝方法優雅地關閉所有Closeable實例

closeGraceFully(Closeable c) { // call this guy for all instances of closeable 
    try{ 
     c.close(); 
    } catch(IOException ex) { 
     // nothing can be done here, frankly. 
    } 
} 

然後調用這個包裝方法。請勿直接撥打close()。不要使用finalizers他們是邪惡的,會放慢你的應用程序。