101

有没有一种优雅的方法来处理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/ ?catchfinally

4

15 回答 15

73

我通常这样做:

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 );
  }
}
于 2009-01-26T21:55:35.347 回答
26

我通常使用以下closeQuietly方法之一org.apache.commons.io.IOUtils

public static void closeQuietly(OutputStream output) {
    try {
        if (output != null) {
            output.close();
        }
    } catch (IOException ioe) {
        // ignore
    }
}
于 2009-01-26T23:16:37.680 回答
22

如果您使用的是 Java 7,并且resourceimplements AutoClosable,您可以这样做(以 InputStream 为例):

try (InputStream resource = getInputStream()) {
  // Use the resource.
}
catch( Exception ex ) {
  // Problem with the resource.
}
于 2012-02-09T06:55:52.443 回答
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 他更进一步,将两个异常合二为一,我认为这在某些情况下很有用。

于 2011-06-14T08:57:23.683 回答
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

于 2015-04-09T16:06:02.077 回答
3

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

我个人的倾向是让 finally 方法捕获发生的异常并将它们包装在“CleanupFailedException”中,认识到此类失败代表一个主要问题,不应轻易捕获此类异常。

于 2012-02-09T20:45:01.193 回答
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 */ }
 }
于 2009-01-26T21:35:11.753 回答
1

为什么要避免附加块?由于 finally 块包含可能引发异常的“正常”操作,并且您希望 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) {
    ...
}
于 2009-01-26T21:42:03.377 回答
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
        }
}

该代码保证以下内容:

  1. 代码完成后资源被释放
  2. 关闭资源时抛出的异常不会在不处理它们的情况下被消耗。
  3. 代码不会尝试关闭资源两次,不会产生不必要的异常。
于 2012-01-17T13:36:50.850 回答
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 块包围您的代码(尽管我建议仍然捕获特定的预期错误)。

于 2009-01-26T21:39:29.360 回答
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(); 
    }
  }
}
于 2009-01-26T21:50:30.210 回答
0

我通常这样做:

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

理由:如果我完成了资源并且我唯一的问题是关闭它,那么我对此无能为力。如果我已经完成了资源,那么杀死整个线程也没有任何意义。

这是一种情况,至少对我来说,忽略该检查异常是安全的。

直到今天,我在使用这个成语时还没有遇到任何问题。

于 2009-01-26T22:40:58.267 回答
0
try {
    final Resource resource = acquire();
    try {
        use(resource);
    } finally {
        resource.release();
    }
} catch (ResourceException exx) {
    ... sensible code ...
}

任务完成。没有空测试。单次捕获,包括获取和释放异常。当然,您可以使用 Execute Around 习惯用法,并且只需为每种资源类型编写一次。

于 2009-01-27T13:50:57.633 回答
0

Resource最佳答案更改为Closeable

Streams implementsCloseable因此,您可以为所有流重用该方法

protected void closeQuietly(Closeable resource) {
    if (resource == null) 
        return;
    try {
        resource.close();
    } catch (IOException e) {
        //log the exception
    }
}
于 2017-12-15T09:11:32.880 回答
0

我遇到了类似的情况,我无法对资源使用 try 但我也想处理来自关闭的异常,而不仅仅是像 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);
}
于 2020-02-14T15:07:10.457 回答