抛出exception终于块

有没有一个优雅的方式来处理finally拦截的exception?

例如:

 try { // Use the resource. } catch( Exception ex ) { // Problem with the resource. } finally { try{ resource.close(); } catch( Exception ex ) { // Could not close the resource? } } 

你如何避免finally块中的try / catch

我通常这样做:

 try { // Use the resource. } catch( Exception ex ) { // Problem with the resource. } finally { // Put away the resource. closeQuietly( resource ); } 

别处:

 protected void closeQuietly( Resource resource ) { try { if (resource != null) { resource.close(); } } catch( Exception ex ) { log( "Exception during Resource.close()", ex ); } } 

我通常使用org.apache.commons.io.IOUtils中的closeQuietly方法之一:

 public static void closeQuietly(OutputStream output) { try { if (output != null) { output.close(); } } catch (IOException ioe) { // ignore } } 

如果您使用的是Java 7,并且resource实现了AutoClosable ,那么可以这样做(使用InputStream作为示例):

 try (InputStream resource = getInputStream()) { // Use the resource. } catch( Exception ex ) { // Problem with the resource. } 

可以说有点过于顶部,但如果你让exception冒出来,而且你不能logging你的方法中的任何东西(例如,因为它是一个库,你宁愿让调用代码处理exception和日志logging)也许有用:

 Resource resource = null; boolean isSuccess = false; try { resource = Resource.create(); resource.use(); // Following line will only run if nothing above threw an exception. isSuccess = true; } finally { if (resource != null) { if (isSuccess) { // let close throw the exception so it isn't swallowed. resource.close(); } else { try { resource.close(); } catch (ResourceException ignore) { // Just swallow this one because you don't want it // to replace the one that came first (thrown above). } } } } 

更新:我多看了一下,发现了一个很好的博客文章,这个博客文章比我更清楚地想到了这个: http : //illegalargumentexception.blogspot.com/2008/10/java-how-not-to-make -mess-of-stream.html他更进一步,将两个exception合并为一个,在某些情况下,我可以看到这些exception是有用的。

从Java 7开始,您不再需要显式closuresfinally块中的资源,而是可以使用try -with-resources语法。 try-with-resources语句是声明一个或多个资源的try语句。 资源是程序结束后必须closures的对象。 try-with-resources语句确保每个资源在语句结束时closures。 任何实现java.lang.AutoCloseable的对象(包括所有实现java.io.Closeable的对象)都可以用作资源。

假设下面的代码:

 try( Connection con = null; Statement stmt = con.createStatement(); Result rs= stmt.executeQuery(QUERY);) { count = rs.getInt(1); } 

如果发生任何exception, close方法将按照创build它们的相反顺序在这三个资源中的每一个上调用。 这意味着close方法将首先调用ResultSetm,然后调用Statement,最后调用Connection对象。

知道closures方法自动调用时发生的任何exception都被抑制,这一点也很重要。 这些抑制的exception可以通过在Throwable类中定义的getsuppressed()方法来检索。

来源: https : //docs.oracle.com/javase/tutorial/essential/exceptions/tryResourceClose.html

一个解决scheme,如果两个例外是两个不同的类

 try { ... } catch(package1.Exception err) { ... } catch(package2.Exception err) { ... } finally { } 

但是有时你无法避免第二次尝试。 例如closuresstream

 InputStream in=null; try { in= new FileInputStream("File.txt"); (..)// do something that might throw an exception during the analysis of the file, eg a SQL error } catch(SQLException err) { //handle exception } finally { //at the end, we close the file if(in!=null) try { in.close();} catch(IOException err) { /* ignore */ } } 

忽略在“finally”块中发生的exception通常是一个坏主意,除非知道这些exception将会是什么,它们将代表什么条件。 在正常的try/finally使用模式中, try块将事物置于外部代码不期望的状态, finally块将这些事情恢复到外部代码所期望的状态。 捕获exception的外部代码通常会期望,尽pipe例外,所有事情都已恢复到normal状态。 例如,假设某些代码启动一个事务,然后尝试添加两条logging; “finally”块执行“如果未提交回滚”操作。 调用者可能会准备在执行第二个“添加”操作期间发生exception,并且可能预料到,如果它捕获到这样的exception,数据库将处于尝试执行任何操作之前的状态。 但是,如果在回滚期间发生第二个exception,则调用者对数据库状态做出任何假设时可能会发生错误。 回滚失败代表了一个重大的危机 – 不应该被代码所期望的仅仅是“无法添加logging”的例外。

我个人的倾向是有一个finally方法捕获发生的exception,并将它们包装在一个“CleanupFailedException”中,认识到这样的失败代表了一个主要问题,这样的exception不应该被忽略。

为什么你想避免额外的块? 由于finally块包含可能会抛出exception的“正常”操作,并且您希望finally块完全运行,所以您必须捕获exception。

如果你不希望finally块抛出一个exception,并且你不知道如何处理这个exception(你只需要转储堆栈跟踪)就让这个exception向上调用堆栈(从finally中移除try-catch块)。

如果你想减less键入,你可以实现一个“全局”外try-catch块,它将捕获finally块中抛出的所有exception:

 try { try { ... } catch (Exception ex) { ... } finally { ... } try { ... } catch (Exception ex) { ... } finally { ... } try { ... } catch (Exception ex) { ... } finally { ... } } catch (Exception ex) { ... } 

经过多番考虑,我发现以下代码最好:

 MyResource resource = null; try { resource = new MyResource(); resource.doSomethingFancy(); resource.close(); resource = null; } finally { closeQuietly(resource) } void closeQuietly(MyResource a) { if (a!=null) try { a.close(); } catch (Exception e) { //ignore } } 

该代码保证以下内容:

  1. 代码完成后资源被释放
  2. closures资源时引发的exception不消耗,不处理它们。
  3. 代码不尝试closures资源两次,没有不必要的exception将被创build。

如果你可以,你应该testing,以避免错误状况开始。

 try{...} catch(NullArgumentException nae){...} finally { //or if resource had some useful function that tells you its open use that if (resource != null) { resource.Close(); resource = null;//just to be explicit about it was closed } } 

另外你也许应该只捕捉你可以从中恢复的exception,如果你不能恢复,那就让它传播到你的程序的顶层。 如果你不能testing一个错误的条件,你将不得不用一个try catch代码块来包围你的代码,就像你已经完成的那样(虽然我会build议你仍然捕获特定的,预期的错误)。

你可以重构这另一种方法…

 public void RealDoSuff() { try { DoStuff(); } catch { // resource.close failed or something really weird is going on // like an OutOfMemoryException } } private void DoStuff() { try {} catch { } finally { if (resource != null) { resource.close(); } } } 

我通常这样做:

 MyResource r = null; try { // use resource } finally { if( r != null ) try { r.close(); } catch( ThatSpecificExceptionOnClose teoc ){} } 

理由:如果我已经完成了这个资源,唯一的问题就是closures它,那我就不能做了。 无论如何,如果我已经完成了资源,那么杀死整个线程也是没有意义的。

这是至less对我来说,忽略检查exception是安全的。

直到今天,我还没有使用这个习语的任何问题。

 try { final Resource resource = acquire(); try { use(resource); } finally { resource.release(); } } catch (ResourceException exx) { ... sensible code ... } 

任务完成。 没有空testing。 一次捕获,包括获得和释放例外。 当然,你可以使用Execute Around idiom,只需要为每个资源types编写一次。