finalize 方法、可關閉接口和 try-with-resources 語句 (Java 7) - 1

“嗨,阿米戈!”

“我只是決定和你討論一下finalize ()方法。”

“如果你還記得的話,finalize() 是一個特殊的方法,在垃圾收集器銷毀它之前由對象調用。”

“此方法的主要目的是通過關閉文件、I/O 流等來釋放已使用的外部非 Java 資源。”

“不幸的是,這個方法沒有達到我們的預期。Java 虛擬機可以推遲銷毀對象,也可以推遲調用 finalize 方法,只要它願意。而且,它不保證這個方法會被執行根本沒有被調用。有很多情況下它沒有被調用,都是以 «優化» 的名義。”

“我有兩個推薦信給你:”

Joshua Bloch 寫了一篇關於此方法的好文章:鏈接
我將轉述一小段摘錄:

  1. finalize() 只能在兩種情況下使用:
    1. 用於使用日誌記錄驗證或清理資源。
    2. 使用對資源洩漏不重要的本機代碼時。
  2. finalize() 使 GC 在清理對象時慢 430 倍
  3. finalize() 可能不會被調用
如果我在採訪中說 finalize 是一個有害且危險的拐杖,其存在本身就令人困惑,我是對的嗎?

“好吧,這讓我很高興,艾莉。”

“Java 7 有一個新語句來替換finalize方法。它稱為try-with-resources。它並不是finalize的真正替代品,而是一種替代方法。”

“它是不是像 try-catch,但有資源?”

“這幾乎就像try-catch。事情是,與finalize () 方法不同,try- catch-finally語句中的finally塊總是被執行。程序員在需要釋放資源時也使用了這種技術,關閉線程等。
 “這是一個例子:”

InputStream is = null;
try
{
 is = new FileInputStream("c:/file.txt");
 is.read(…);
}
finally
{
 if (is != null)
 is.close();
}

“無論try塊是否正常執行或出現異常,finally塊都會被調用,並且有可能在那裡釋放佔用的資源。”

“因此,在 Java 7 中,決定將這種方法正式化,如下所示:”

try(InputStream is = new FileInputStream("c:/file.txt"))
{
 is.read(…);
}

“這個特殊的try語句稱為try-with-resources(類似於集合如何稱為foreach的替代方法)。”

“注意在try之後有括號,其中聲明變量和創建對象。這些對象可以在大括號指示的try塊內使用。當try塊執行完成時,無論它是正常結束還是在那裡是一個例外,將在括號內創建的任何對像上調用 close() 方法。”

“真有趣!這個符號比之前的符號要緊湊得多。我不確定我是否理解它。”

“沒你想的那麼難。”

“那麼,我可以在括號中指定每個對象的類別嗎?”

“是的,當然,否則括號就沒有什麼用了。”

“如果我需要在退出 try 塊後調用另一個方法,我應該把它放在哪裡?”

“這裡的事情有點微妙。Java 7 引入了以下接口:”

public interface AutoCloseable
{
 void close() throws Exception;
}

“你的類可以實現這個接口。然後你可以在 try-with-resources 語句中使用它的對象。只有這樣的對象才能在 try-with-resources 語句的括號內創建,以實現 «automatic closure»。

“換句話說,我需要覆蓋 close 方法並在其中編寫代碼來 «clean up» 我的對象,我不能指定另一個方法嗎?”

“是的。但您可以指定多個對象——只需用分號分隔它們:”

try(
InputStream is = new FileInputStream("c:/file.txt");
OutputStream os = new FileOutputStream("c:/output.txt")
)
{
 is.read(…);
 os.write(…);
}

“那好多了,但沒有我希望的那麼酷。”

“沒那麼糟糕。你會習慣的。隨著時間的推移。”