首页 文章

在finally块中抛出异常

提问于
浏览
96

是否有一种优雅的方法来处理 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

14 回答

  • 0

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

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

    Resourcebest answer更改为Closeable

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

    protected void closeQuietly(Closeable resource) {
        if (resource == null) 
            return;
        try {
            resource.close();
        } catch (IOException e) {
            //log the exception
        }
    }
    
  • 1
    try {
        final Resource resource = acquire();
        try {
            use(resource);
        } finally {
            resource.release();
        }
    } catch (ResourceException exx) {
        ... sensible code ...
    }
    

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

  • 6

    如果您正在使用Java 7,并且 resource 实现 AutoClosable ,则可以执行此操作(使用InputStream作为示例):

    try (InputStream resource = getInputStream()) {
      // Use the resource.
    }
    catch( Exception ex ) {
      // Problem with the resource.
    }
    
  • 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
            }
    }
    

    该代码保证以下内容:

    • 代码完成后释放资源

    • 关闭资源时抛出的异常不会在不处理的情况下消耗掉 .

    • 代码不会尝试关闭资源两次,也不会创建不必要的异常 .

  • 0

    我通常这样做:

    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 );
      }
    }
    
  • 0

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

    我个人倾向于有一个finally方法捕获发生的异常并将它们包装在“CleanupFailedException”中,认识到这种失败代表了一个主要问题,并且不应该轻易 grab 这样的异常 .

  • 3

    我经常这样做:

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

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

    这是至少对我而言,忽略该检查异常是安全的情况之一 .

    直到今天,我还没有使用这个成语的任何问题 .

  • 0

    一种解决方案,如果两个异常是两个不同的类

    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 */ }
     }
    
  • 22

    你可以将它重构成另一种方法......

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

    如果可以,您应该进行测试以避免错误情况开始 .

    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块来包围代码,就像您已经完成的那样(尽管我建议仍然捕获特定的,预期的错误) .

  • 0

    从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

  • 2

    可以说有点超过顶部,但如果你让异常冒出来并且你无法记录任何内容,那么它可能很有用在你的方法中(例如,因为它是一个库,你宁愿让调用代码处理异常和日志记录):

    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他更进了一步,将两个例外结合成一个,我认为在某些情况下有用 .

  • 71

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

相关问题