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(…);
}

“那好多了,但没有我希望的那么酷。”

“没那么糟糕。你会习惯的。随着时间的推移。”