抛出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 } }
该代码保证以下内容:
- 代码完成后资源被释放
- closures资源时引发的exception不消耗,不处理它们。
- 代码不尝试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编写一次。