是否有一个嵌套try / catch块的偏好?

在Java中使用Readers和Streams总是让我感到困惑的事情之一是close()方法可能会抛出一个exception。 因为将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 } } 

我更喜欢第一个build筑,因为只有一个catch块,它看起来更优雅。 是否有理由更喜欢第二个或替代build设?

更新:如果我指出readclose只抛出IOExceptions会有所作为吗? 所以,对我来说,如果读取失败,closures将失败,出于同样的原因。

我会永远为第一个例子。

如果closures是抛出一个exception(在实践中,这是永远不会发生的FileReader),不是标准的处理方式是抛出一个适当的exception调用者? closuresexception几乎肯定胜过你使用资源的任何问题。 如果你的exception处理的想法是调用System.err.println,第二种方法可能更合适。

有一个问题应该抛出多less例外。 ThreadDeath应该总是被重新抛出,但是finally中的任何exception都会阻止它。 同样,错误应该比RuntimeException和RuntimeException进一步超出检查exception。 如果你真的想要你可以编写代码来遵循这些规则,然后用“执行周围”的成语来抽象它。

恐怕第一个例子有一个很大的问题,那就是如果读或者读之后发生exception, finally块就会执行。 到现在为止还挺好。 但是,如果fr.close()会导致另一个exception被抛出呢? 这将会“超越”第一个exception(有点像把return放在finally块中), 你将会失去关于实际上导致问题开始的所有信息

你的finally块应该使用:

 IOUtil.closeSilently(fr); 

这个实用方法只是这样做的:

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

我更喜欢第二个。 为什么? 如果read()close()抛出exception,其中一个可能会丢失。 在第一个构造中, close()的exception会覆盖read()的exception,而第二个exception则是close()的exception。


从Java 7开始, try-with-resources结构使得这更简单。 阅读而不关心例外:

 try (FileReader fr = new FileReader("SomeFile.txt")) { fr.read(); // no need to close since the try-with-resources statement closes it automatically } 

有exception处理:

 try (FileReader fr = new FileReader("SomeFile.txt")) { fr.read(); // no need to close since the try-with-resources statement closes it automatically } catch (IOException e) { // Do exception handling log(e); // If this catch block is run, the FileReader has already been closed. // The exception could have come from either read() or close(); // if both threw exceptions (or if multiple resources were used and had to be closed) // then only one exception is thrown and the others are suppressed // but can still be retrieved: Throwable[] suppressed = e.getSuppressed(); // can be an empty array for (Throwable t : suppressed) { log(suppressed[t]); } } 

只需要一次尝试就可以安全地处理所有exception。 如果你喜欢,你仍然可以添加一个finally块,但是不需要closures资源。

如果读取closures都抛出exception,则读取的exception将隐藏在选项1中。所以,第二个选项会执行更多的error handling。

但是,在大多数情况下,第一个选项仍然是首选。

  1. 在许多情况下,您不能在生成方法中处理exception,但仍然必须在该操作中封装stream处理。
  2. 尝试添加一个作家的代码,看看第二种方法得到多么详细。

如果您需要传递所有生成的exception, 则可以完成 。

不同的是,就我所知,不同的层面上有不同的例外和原因,

捕获(例外e)

模糊了这一点。 多层次的唯一的一点是区分你的例外,你会怎么做:

 try { try{ ... } catch(IOException e) { .. } } catch(Exception e) { // we could read, but now something else is broken ... } 

我通常做以下。 首先,定义一个基于模板方法的类来处理try / catch混乱

 import java.io.Closeable; import java.io.IOException; import java.util.LinkedList; import java.util.List; public abstract class AutoFileCloser { private static final Closeable NEW_FILE = new Closeable() { public void close() throws IOException { // do nothing } }; // the core action code that the implementer wants to run protected abstract void doWork() throws Throwable; // track a list of closeable thingies to close when finished private List<Closeable> closeables_ = new LinkedList<Closeable>(); // mark a new file protected void newFile() { closeables_.add(0, NEW_FILE); } // give the implementer a way to track things to close // assumes this is called in order for nested closeables, // inner-most to outer-most protected void watch(Closeable closeable) { closeables_.add(0, closeable); } public AutoFileCloser() { // a variable to track a "meaningful" exception, in case // a close() throws an exception Throwable pending = null; try { doWork(); // do the real work } catch (Throwable throwable) { pending = throwable; } finally { // close the watched streams boolean skip = false; for (Closeable closeable : closeables_) { if (closeable == NEW_FILE) { skip = false; } else if (!skip && closeable != null) { try { closeable.close(); // don't try to re-close nested closeables skip = true; } catch (Throwable throwable) { if (pending == null) { pending = throwable; } } } } // if we had a pending exception, rethrow it // this is necessary b/c the close can throw an // exception, which would remove the pending // status of any exception thrown in the try block if (pending != null) { if (pending instanceof RuntimeException) { throw (RuntimeException) pending; } else { throw new RuntimeException(pending); } } } } } 

请注意“待处理”exception – 这将处理在closures期间抛出的exception会掩盖我们可能真正关心的exception的情况。

finally试图从任何装饰stream的外部closures,所以如果你有一个BufferedWriter包装一个FileWriter,我们首先closuresBuffereredWriter,如果失败,仍然尝试closuresFileWriter本身。

你可以使用上面的类如下:

 try { // ... new AutoFileCloser() { @Override protected void doWork() throws Throwable { // declare variables for the readers and "watch" them FileReader fileReader = null; BufferedReader bufferedReader = null; watch(fileReader = new FileReader("somefile")); watch(bufferedReader = new BufferedReader(fileReader)); // ... do something with bufferedReader // if you need more than one reader or writer newFile(); // puts a flag in the FileWriter fileWriter = null; BufferedWriter bufferedWriter = null; watch(fileWriter = new FileWriter("someOtherFile")); watch(bufferedWriter = new BufferedWriter(fileWriter)); // ... do something with bufferedWriter } }; // .. other logic, maybe more AutoFileClosers } catch (RuntimeException e) { // report or log the exception } 

使用这种方法,你永远不用担心try / catch / finally来处理再次closures文件。

如果这太重了,至less要考虑try / catch和它使用的“pending”variables的方法。

我使用的标准约定是,你不能让exception逃脱一个finally块。

这是因为如果一个exception已经传播,抛出finally块的exception会超过原来的exception(从而丢失)。

在99%的情况下,这不是你想要的,因为最初的exception可能是你的问题的根源(任何次要exception都可能是第一次的副作用,但会掩盖你find原始exception源的能力,问题)。

所以你的基本代码应该是这样的:

 try { // Code } // Exception handling finally { // Exception handling that is garanteed not to throw. try { // Exception handling that may throw. } // Optional Exception handling that should not throw finally() {} } 

第二种方法。

否则,我没有看到你从FileReader构造函数捕获exception

http://java.sun.com/j2se/1.5.0/docs/api/java/io/FileReader.html#FileReader(java.lang.String);

公共FileReader(string文件名)抛出FileNotFoundException

所以,我通常在try块中也有构造函数。 在尝试closures之前,finally块会检查读者是否为空。

数据源,连接,语句,结果集相同的模式。

有时嵌套try-catch不是一个偏好,请考虑这一点:

 try{ string s = File.Open("myfile").ReadToEnd(); // my file has a bunch of numbers // I want to get a total of the numbers int total = 0; foreach(string line in s.split("\r\n")){ try{ total += int.Parse(line); } catch{} } catch{} 

这可能是一个不好的例子,但是有时候你需要嵌套的try-cactch。

我喜欢@Chris Marshall的方法,但我从不喜欢看到例外被无声地吞噬。 我认为最好是loggingexception情况,特别是不pipe你是否在争论。

我总是用一个实用程序类来处理这些常见的exception,但是我会让他的答案变得很微妙。

我总是会使用一个logging器(log4j)来logging错误等。

 IOUtil.close(fr); 

对实用程序方法稍作修改:

 public static void close(Closeable c) { try { c.close(); } catch (Exception e) { logger.error("An error occurred while closing. Continuing regardless", e); } } 

在某些情况下,嵌套的Try-Catch是不可避免的。 例如,当错误恢复代码本身可以抛出和exception。 但为了提高代码的可读性,您总是可以将嵌套块抽取到自己的方法中。 查看这篇博客文章,了解嵌套Try-Catch-Finally块的更多示例。