我有一些关于 java finalization 的问题。例如,我有一个类 FileHelper,这个类与读取文件、写入文件等相关联。现在我的问题是,我在 FileHelper 类中有一个方法 writeFile()。现在如果我想关闭该文件,我应该覆盖 finalize() 方法并关闭文件,还是可以在 writeFile() 方法本身中关闭文件?哪种方法是正确的?我已将我的 File 变量声明为成员变量。如果重写是个坏主意,那我们为什么要重写 finalize() 方法呢?哪个场景?我读过很多文章,他们说要关闭文件、字体等系统资源。
4 回答
最佳做法是尽快关闭文件。如果您在 FileHelper 中有静态方法(假设您的 Helper 是一堆静态“帮助”方法)我会关闭里面的文件
static void writeFile(String fileName, String text) { // Exception
// Open file here
// write text
// close it
}
覆盖 finalize 的目的是释放非托管资源,例如文件,以防有人忘记这样做。如果有人以这种方式使用你的助手
FileHelper fileHelper = new FileHelper(file);
fileHelper.writeFile(text);
// forgot to fileHelper.close();
并且您已经覆盖了 finalize 并在 GC 运行时在内部调用 close() 文件将被关闭。问题:
- 正如我之前提到的,文件应该尽快关闭(但不能更早;))
- 这是不确定的(没有人知道 GC 何时开始)
- 这应该仅用于防止调用者忘记关闭文件的情况
在几乎所有情况下,使用终结器都是一个坏主意。Java 规范声明不能保证终结器会一直运行。即使他们这样做,您也无法控制何时发生这种情况。
使用终结器作为关闭文件的主要机制总是一个坏主意。为什么?因为如果 GC 长时间不运行,您的应用程序很可能会耗尽文件描述符,并且文件打开尝试将开始失败。
处理打开的流的最佳方法是将它们保存在局部变量和参数中,并用于try { ...} finally
确保它们在完成后始终关闭。或者在 Java 7 中,使用新的“try with resource”语法。
如果您需要将流放在成员变量中,您可能应该让父类实现一个close()
关闭流的方法,并用于try { ...} finally
确保父类的实例被关闭。
还应该注意的是,使用终结器来关闭“丢失”的流几乎没有意义。使用需要关闭的外部资源的流类已经有终结器来执行此操作。
finalize() 方法只会在 Java 垃圾收集器即将回收对象时被调用。在 finalize 方法中释放文件句柄是一种不好的做法。
它可能会导致 java.io.IOException: Too many open files 因为您无法保证垃圾收集器何时运行。
更好的选择是在 finally 块中关闭文件读取器/写入器对象。因为它保证运行。
finally {
// Always close input and output streams. Doing this closes
// the channels associated with them as well.
try {
if (fin != null)
fin.close();
if (fout != null)
fout.close();
} catch (IOException e) {
}}
fin 和 fout 是 FileInputStream 和 FileOutputStream 对象。
覆盖 finalize() 不是一个好习惯。我会在代码中完成它。