是否有一种优雅的方法来处理finally
块中引发的异常?
例如:
try {
// Use the resource.
}
catch( Exception ex ) {
// Problem with the resource.
}
finally {
try{
resource.close();
}
catch( Exception ex ) {
// Could not close the resource?
}
}
如何避免try
区块中的catch
/ finally
?
答案 0 :(得分:71)
我通常这样做:
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 );
}
}
答案 1 :(得分:25)
我通常使用closeQuietly
中的org.apache.commons.io.IOUtils
方法之一:
public static void closeQuietly(OutputStream output) {
try {
if (output != null) {
output.close();
}
} catch (IOException ioe) {
// ignore
}
}
答案 2 :(得分:22)
如果您使用的是Java 7,并且resource
实现了AutoClosable
,则可以执行此操作(使用InputStream作为示例):
try (InputStream resource = getInputStream()) {
// Use the resource.
}
catch( Exception ex ) {
// Problem with the resource.
}
答案 3 :(得分:8)
可以说有点超过顶部,但是如果你让异常冒出来并且你不能在你的方法中记录任何东西(例如因为它是一个库而你宁愿让调用代码处理异常,那么也许有用)记录):
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他更进了一步,将两个例外合并为一个,我可以看到在某些情况下有用。
答案 4 :(得分:6)
从Java 7开始,您不再需要明确关闭 finally 块中的资源,而是可以使用 try -with-resources语法。 try-with-resources语句是一个声明一个或多个资源的try语句。资源是在程序完成后必须关闭的对象。 try-with-resources语句确保在语句结束时关闭每个资源。实现java.lang.AutoCloseable的任何对象(包括实现java.io.Closeable的所有对象)都可以用作资源。
假设以下代码:
try( Connection con = null;
Statement stmt = con.createStatement();
Result rs= stmt.executeQuery(QUERY);)
{
count = rs.getInt(1);
}
如果发生任何异常,将按照创建它们的相反顺序在这三个资源中的每一个上调用 close 方法。这意味着将首先为ResultSetm调用close方法,然后调用Statement,最后调用Connection对象。
知道自动调用close方法时发生的任何异常都会被抑制,这一点也很重要。可以通过 Throwable 类中定义的 getsuppressed()方法检索这些抑制的异常。
来源:https://docs.oracle.com/javase/tutorial/essential/exceptions/tryResourceClose.html
答案 5 :(得分:3)
忽略在'finally'块中发生的异常通常是一个坏主意,除非知道这些异常是什么以及它们将代表什么条件。在正常try/finally
使用模式中,try
块将事物置于外部代码不期望的状态,finally
块将那些事物的状态恢复为外部代码预计。捕获异常的外部代码通常会期望,尽管有异常,但所有内容都已恢复到normal
状态。例如,假设某些代码启动一个事务,然后尝试添加两个记录; “finally”块执行“如果未提交则回滚”操作。调用者可能已准备好在执行第二个“添加”操作期间发生异常,并且可能期望如果它捕获到这样的异常,则数据库将处于尝试任一操作之前的状态。但是,如果在回滚期间发生第二个异常,则如果调用者对数据库状态做出任何假设,则可能会发生错误。回滚失败代表了一个主要的危机 - 一个不应该被期望仅仅是“添加记录失败”异常的代码捕获的危机。
我个人倾向于使用finally方法捕获发生的异常,并将它们包装在“CleanupFailedException”中,认识到这种失败代表了一个主要问题,并且不应该轻易抓住这样的异常。
答案 6 :(得分:2)
一种解决方案,如果两个例外是两个不同的类
try {
...
}
catch(package1.Exception err)
{
...
}
catch(package2.Exception err)
{
...
}
finally
{
}
但有时你无法避免这第二次尝试。例如关闭流
InputStream in=null;
try
{
in= new FileInputStream("File.txt");
(..)// do something that might throw an exception during the analysis of the file, e.g. 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 */ }
}
答案 7 :(得分:1)
为什么要避免额外的阻止?由于finally块包含可能引发异常的“正常”操作,并且您希望finally块完全运行,因此您必须捕获异常。
如果你不希望finally块抛出异常并且你不知道如何处理异常(你只是转储堆栈跟踪)让异常冒泡调用堆栈(删除try-catch)从最后一块)。
如果你想减少输入,你可以实现一个“全局”外部try-catch块,它将捕获finally块中抛出的所有异常:
try {
try {
...
} catch (Exception ex) {
...
} finally {
...
}
try {
...
} catch (Exception ex) {
...
} finally {
...
}
try {
...
} catch (Exception ex) {
...
} finally {
...
}
} catch (Exception ex) {
...
}
答案 8 :(得分:1)
经过多次考虑后,我发现以下代码最佳:
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
}
}
该代码保证以下内容:
答案 9 :(得分:0)
如果可以,您应该进行测试以避免错误情况开始。
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
}
}
此外,您应该只捕获可以恢复的异常,如果无法恢复,则将其传播到程序的顶层。如果你不能测试一个错误条件,你必须用你已经完成的try catch块来包围你的代码(尽管我建议仍然捕获特定的预期错误)。
答案 10 :(得分:0)
您可以将其重构为另一种方法......
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();
}
}
}
答案 11 :(得分:0)
我通常这样做:
MyResource r = null;
try {
// use resource
} finally {
if( r != null ) try {
r.close();
} catch( ThatSpecificExceptionOnClose teoc ){}
}
理由:如果我已经完成了资源并且我唯一的问题是关闭它,那么我无能为力。如果我已经完成了资源,那么杀死整个线程是没有意义的。
这是至少对我而言,忽略该检查异常是安全的情况之一。
直到今天,我还没有使用这个成语的任何问题。
答案 12 :(得分:0)
try {
final Resource resource = acquire();
try {
use(resource);
} finally {
resource.release();
}
} catch (ResourceException exx) {
... sensible code ...
}
完成工作。没有空测试。单一捕获,包括获取和释放异常。当然,您可以使用Execute Around惯用法,只需为每种资源类型编写一次。
答案 13 :(得分:0)
将Resource
从best answer更改为Closeable
Streams implements Closeable
因此,您可以为所有流重用该方法
protected void closeQuietly(Closeable resource) {
if (resource == null)
return;
try {
resource.close();
} catch (IOException e) {
//log the exception
}
}
答案 14 :(得分:0)
我遇到过类似的情况,我无法对资源使用try,但我也想处理close发出的异常,而不仅仅是像closeQuietly机制那样记录并忽略它。就我而言,我实际上并没有处理输出流,因此,关闭失败比简单流更令人感兴趣。
IOException ioException = null;
try {
outputStream.write("Something");
outputStream.flush();
} catch (IOException e) {
throw new ExportException("Unable to write to response stream", e);
}
finally {
try {
outputStream.close();
} catch (IOException e) {
ioException = e;
}
}
if (ioException != null) {
throw new ExportException("Unable to close outputstream", ioException);
}