题
是否有一个优雅的方式来处理异常在 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
块?
解决方案
我通常做这样的:
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 );
}
}
其他提示
我通常使用的一个 closeQuietly
方法 org.apache.commons.io.IOUtils
:
public static void closeQuietly(OutputStream output) {
try {
if (output != null) {
output.close();
}
} catch (IOException ioe) {
// ignore
}
}
如果你使用Java7, resource
实现了 AutoClosable
, ,你可以做到这一点(利用输入流作为实例):
try (InputStream resource = getInputStream()) {
// Use the resource.
}
catch( Exception ex ) {
// Problem with the resource.
}
可以说是有点过了,但也许是有用的,如果你让的例外情况泡了,你可以登录任何东西从你的方法(例如因为这是一个图书馆和你宁愿让调用代码处理的例外情况和记录):
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 他进一步,结合了两种例外情况变成一个,我能看见是有用的,在某些情况下。
作为Java7你不再需要明确地接近资源 最后 方框,而不是可以使用 尝试-与资源的语法。尝试用资源的声明是试图声明,声明一个或更多的资源。资源的一个目的是,必须先关闭程序之后完成。尝试用资源的声明可以确保每个资源是关闭结束时的发言。任何对象实现java。郎。AutoCloseable,其中包括所有对象实施java。io.可封闭的,可以作为一个资源。
假设下列代码:
try( Connection con = null;
Statement stmt = con.createStatement();
Result rs= stmt.executeQuery(QUERY);)
{
count = rs.getInt(1);
}
如果有任何异常情况发生的 靠近 方法将被称作上的这三个资源中相对便在他们所创建的。这意味着紧密的方法将被称为第一ResultSetm然后发言和结束时连接的对象。
同样重要的是要知道,任何例外情况发生时关闭方法是自动的所谓被抑制。这些抑制的例外,可以检索的 getsuppressed() 方法中定义的 抛出 类。
资料来源: https://docs.oracle.com/javase/tutorial/essential/exceptions/tryResourceClose.html
忽略的例外情况发生在一个'最终'块通常是一个 坏主意 除非另有一个知道什么这些例外将是什么样的条件下,他们将代表。在正常 try/finally
使用模式, try
块地方的东西进入一国之外的代码不会期待, finally
框恢复那些东西'国家以外的代码的期望。外码渔获物的一个例外将一般的期望,尽管有例外,一切已经恢复到一个 normal
状态。例如,假设一些代码开始交易,然后试图添加两个记录;将"最后"的框执行"回滚如果不致力于"操作。呼叫者可能是准备用于一个例外发生在执行第二个"添加"操作,并且可以预计,如果它抓住这样一个例外,该数据库将是在所处的状态之前操作的尝试。但是,如果一个第二个例外发生在返回过程中,不好的事情可能发生,如果叫做任何假设的数据库的状态。回退的失败表示 主要的 危机--一个不应该被抓获的代码期望的仅仅是"未能加入记录的"异常。
我个人的倾向将会有一个最后方法赶上发生的异常和包装在一个"CleanupFailedException",认识到这种失败表示的一个主要问题和这样一个例外,不应抓住以轻心。
一个解决方案,如果两个例外是两个不同的类
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 */ }
}
为什么你想要避免的额外块?由于最后块包含的"正常"行动,这可能会引发一个例外和你想的最后框运行,你必须赶上的例外情况。
如果你不要期望最终块扔一个例外,你不知道如何处理例外呢(你只是转储堆栈)让除外泡沫的呼堆(除去试试-赶上从最后块)。
如果你想要减少输入您可以实现一个"全球"外尝试抓住块,它抓住所有的例外情况扔在最后块:
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
}
}
这些代码的保障,如下:
- 资源时释放的代码完成
- 例外扔的时候关闭的资源不消耗没有处理它们。
- 该守则没有试图接近资源的两倍,没有不必要的例外将会被创建。
如果你可以你应该测试,以避免的错误的条件开始。
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
}
}
你也应该可能只被捕的例外情况,你可以从中恢复,如果不能恢复,然后让它传播到顶级的程序。如果你可以不检验一个错误的条件下,你将环绕着你的代码有一个尝试抓住框像你已经完成的(尽管我建议仍然捕捉特定的,预计误差)。
你能重构,这成为另一种方法...
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 ){}
}
理由:如果我这样做的资源和唯一的问题我已经被关闭,没有太多我能做的。这没什么意义么要杀死整个线如果我这样做的资源。
这是其中一个案件时,至少对我来说,这是安全忽略这一检查的例外。
到今天我还没有任何问题采用这种惯用语。
try {
final Resource resource = acquire();
try {
use(resource);
} finally {
resource.release();
}
} catch (ResourceException exx) {
... sensible code ...
}
完成工作。没有空测试。单一的渔获,包括获取和释放的例外情况。当然你也可以使用的执行周围的成语和必须只把它写一旦为每一资源类型。