是否有嵌套尝试/捕获块的首选项?

2022-09-01 02:41:09

在Java中使用Reader和Streams时,总是让我烦恼的一件事是,该方法可能会引发异常。由于将 close 方法放在最终块中是个好主意,因此需要一些尴尬的情况。我通常使用这种结构:close()

FileReader fr = new FileReader("SomeFile.txt");
try {
    try {
        fr.read();
    } finally {
        fr.close();
    }
} catch(Exception e) {
    // Do exception handling
}

但我也看到了这种结构:

FileReader fr = new FileReader("SomeFile.txt");
try {
    fr.read() 
} catch (Exception e) {
    // Do exception handling
} finally {
    try {
        fr.close();
    } catch (Exception e) {
        // Do exception handling
    }
}

我更喜欢第一个结构,因为只有一个捕获块,它看起来更优雅。有没有理由实际上更喜欢第二种或另一种结构?

更新:如果我指出两者并且只抛出IOExceptions,会有什么不同吗?因此,在我看来,如果读取失败,则关闭将因同样的原因而失败。readclose


答案 1

恐怕第一个例子有一个很大的问题,那就是如果在读取时或之后发生异常,则块将执行。目前为止,一切都好。但是,如果 随后导致引发另一个异常,该怎么办?这将“胜过”第一个例外(有点像放入一个块),您将丢失有关实际导致问题的所有信息。finallyfr.close()returnfinally

您的最终块应使用:

IOUtil.closeSilently(fr);

其中,此实用程序方法只是执行:

public static void closeSilently(Closeable c) {
    try { c.close(); } catch (Exception e) {} 
} 

答案 2

我总是会选择第一个例子。

如果 close 要引发异常(实际上,对于 FileReader,这永远不会发生),那么标准的处理方式难道不是抛出适合调用方的异常吗?几乎可以肯定的是,这个例外胜过您在使用资源时遇到的任何问题。第二种方法可能更合适,如果你的异常处理的想法是调用System.err.println。

存在一个问题,即应引发多远的异常。ThreadDeath应该总是被重新抛出,但其中的任何例外最终都会阻止它。类似地,Error 应比 RuntimeException 和 RuntimeException 更远地引发比已检查的异常更远的异常。如果你真的想要,你可以编写代码来遵循这些规则,然后用“执行”这个习语来抽象它。


推荐