Java 在finally块中抛出异常

Java 在finally块中抛出异常,java,exception,try-catch,finally,Java,Exception,Try Catch,Finally,是否有一种优雅的方法来处理finally块中抛出的异常 例如: 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 {
  // 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 {
    ...
    }
catch(package1.Exception err)
   {
    ...
   }
catch(package2.Exception err)
   {
   ...
   }
finally
  {
  }
但有时你无法避免第二次尝试。e、 g.用于关闭流

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{...}
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块来包围代码(尽管我建议您仍然可以捕获特定的预期错误)。

为什么要避免额外的块?由于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) {
    ...
}

您可以将其重构为另一种方法

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(); 
    }
  }
}

我通常是这样做的:

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 );
  }
}
我通常这样做:

MyResource r = null;
try { 
   // use resource
} finally {   
    if( r != null ) try { 
        r.close(); 
    } catch( ThatSpecificExceptionOnClose teoc ){}
}
理由:如果我用完了资源,唯一的问题就是关闭它,我就无能为力了。如果我已经用完了资源,那么杀掉整个线程也是没有意义的

这是一种情况,至少对我来说,忽略选中的异常是安全的


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

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

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

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

可以说有点过头了,但如果您让异常冒泡,并且无法从方法中记录任何内容,则可能会很有用(例如,因为它是一个库,您更愿意让调用代码处理异常和日志记录):


更新:我对此进行了更多的研究,发现有人写了一篇很棒的博文,他显然比我想得更多:他更进一步,将两个例外合并为一个,我认为这在某些情况下很有用。

经过大量考虑,我发现以下代码是最好的:

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
        }
}
该守则保证:

  • 代码完成后,资源将被释放
  • 关闭资源时引发的异常不会在不处理它们的情况下被使用
  • 代码不会尝试关闭资源两次,不会创建不必要的异常

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


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


    我个人倾向于使用finally方法捕获发生的异常,并将它们封装在“CleanupFailedException”中,认识到这样的失败是一个主要问题,不应轻易发现这样的异常。

    从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方法将按创建它们的相反顺序对这三个资源中的每一个调用。这意味着将首先为ResultsTM调用close方法,然后为语句调用close方法,最后为Connection对象调用close方法

    了解自动调用close方法时发生的任何异常都会被抑制,这一点也很重要。这些被抑制的异常可以通过Throwable类中定义的getsupprested()方法检索


    来源:

    资源
    从更改为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 (InputStream resource = getInputStream()) {
      // Use the resource.
    }
    catch( Exception ex ) {
      // Problem with the resource.
    }
    
    try( Connection con = null;
         Statement stmt = con.createStatement();
         Result rs= stmt.executeQuery(QUERY);)
    {  
         count = rs.getInt(1);
    }
    
    protected void closeQuietly(Closeable resource) {
        if (resource == null) 
            return;
        try {
            resource.close();
        } catch (IOException e) {
            //log the exception
        }
    }
    
    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);
    }