【问题标题】:What is a suppressed exception?什么是抑制异常?
【发布时间】:2011-12-12 13:27:09
【问题描述】:

answera question about tail call optimisation 上的评论(用户 soc)提到 Java 7 有一个称为“抑制异常”的新功能,因为“添加了 ARM”(支持 ARM CPU?) .

在这种情况下,什么是“被抑制的异常”?在其他情况下,“被抑制的异常”将是一个被捕获然后被忽略的异常(很少是一个好主意);这显然是不同的东西。

【问题讨论】:

标签: java exception exception-handling try-with-resources


【解决方案1】:

为了澄清 Jon 的回答中的引用,一个方法(每次执行)只能抛出一个异常,但在 try-with-resources 的情况下,可能会抛出多个异常。例如,可能会在块中抛出一个,而另一个可能会从 try-with-resources 提供的隐式 finally 中抛出。

编译器必须确定“真正”抛出哪些。它选择抛出在显式代码(try 块中的代码)中引发的异常,而不是由隐式代码(finally 块)引发的异常。因此,隐式块中抛出的异常被抑制(忽略)。这只发生在多个异常的情况下。

【讨论】:

  • 因此,抑制异常是新功能的结果,这意味着我们在打开文件时不再需要编写繁琐的try...finally 子句和close()ing 文件:stackoverflow.com/questions/3305405/…
  • @KanagaveluSugumar Exception(Exception cause) 构造函数用于将导致的异常包装在另一个可能更具描述性的异常中。但是,在这种情况下,我们谈论的是两个不同的例外,它们没有因果关系。异常 A 不会导致异常 B,因此将一个包裹在另一个包裹中是没有意义的。此外,您假设编码器明确抛出第二个异常并且可以访问第一个异常。如果两者都被库调用抛出,情况就不是这样了。
  • @JohnB 我认为你的说法是错误的。 “编译器必须确定哪些是“真正”抛出的。它选择抛出显式代码中引发的异常(try 块中的代码)“但是编译器只会选择 finally 异常,并且 try 异常将被抑制。
  • @KanagaveluSugumar 每个文档:If an exception is thrown from the try block and one or more exceptions are thrown from the try-with-resources statement, then those exceptions thrown from the try-with-resources statement are suppressed
  • 请注意,抑制的异常不仅对 try-with-resources 有用,而且对自定义代码也有用,通常在 catch 或 finally 子句中,否则另一个异常会隐藏原始异常.
【解决方案2】:

我相信评论者所指的是一个异常,当它在 try-with-resources 块的隐式 finally 块内抛出时,在从 try 块抛出的现有异常的上下文中,该异常被半忽略:

可以从与 try-with-resources 语句关联的代码块中引发异常。在示例 writeToFileZipFileContents 中,可以从 try 块中引发异常,并且当 try-with-resources 语句尝试关闭 ZipFile 和 BufferedWriter 对象时,最多可以引发两个异常。如果从 try 块中抛出异常,并且从 try-with-resources 语句中抛出一个或多个异常,则从 try-with-resources 语句中抛出的那些异常被抑制,并且块抛出的异常就是那个由 writeToFileZipFileContents 方法抛出。您可以通过从 try 块抛出的异常中调用 Throwable.getSuppressed 方法来检索这些被抑制的异常。

(这是在链接页面中引用了一个名为“Suppressed Exceptions”的部分。)

【讨论】:

  • @Raedwald:大多数情况下,是的,我想是的。
  • @JonSkeet @Raedwald :我相信这个答案没有考虑到在 Java 7 之前存在被抑制的异常(我不是在谈论 ignored 异常):如果 finallytry 块也抛出异常时,块也会抛出异常,来自try 块的原始异常会丢失或“被抑制”(有关更多信息,请参阅http://accu.org/index.php/journals/236)。 Java 7 刚刚添加了一个方便的方法来存储来自 finally 块的异常,因为 finally 是由 try-with-resources 隐式生成的。
  • 但需要注意的一点是,如果我们在 try-with-resource 语句中显式提供 finally 块并从中抛出异常,则它优先于从 try 或 try-with-resource 块中抛出的异常.
【解决方案3】:

Java7 之前;代码中抛出了异常,但不知何故被忽略了。

例如)

public class SuppressedExceptions {
  public static void main(String[] args) throws Exception {
    try {
        callTryFinallyBlock();
    } catch (Exception e) {
        e.printStackTrace(); **//Only Finally Exception is Caught**
    }
  }

  private static void callTryFinallyBlock() throws Exception {
    try 
    {
        throw new TryException(); **//This is lost**
    }
    finally
    {
        FinallyException fEx = new FinallyException();
        throw fEx;
    }
  }
}

class TryException extends Exception {
}

class FinallyException extends Exception {
}

JDK 7 中的 Throwable 类添加了一个新的构造函数和两个新方法。 如下:

Throwable.getSupressed(); // Returns Throwable[]
Throwable.addSupressed(aThrowable);

使用这种新方法,我们也可以处理那些被抑制的异常。

public class SuppressedExceptions {
  public static void main(String[] args) throws Exception {
    try {
        callTryFinallyBlock();
    } catch (Exception e) {
        e.printStackTrace();
        for(Throwable t: e.getSuppressed())
        {
            t.printStackTrace();
        }
    }
  }

  private static void callTryFinallyBlock() throws Exception {
    Throwable t = null;
    try 
    {
        throw new TryException();
    }
    catch (Exception e) {
        t = e;
    }
    finally
    {
        FinallyException fEx = new FinallyException();
        if(t != null)fEx.addSuppressed(t);
        throw fEx;
    }
  }
}

class TryException extends Exception {
}

class FinallyException extends Exception {
}

在 Java7 中尝试使用资源; AutoCloseable::close() 的异常 默认情况下与 try 异常一起添加为抑制异常。

还请注意,这与 chained exceptions 不同(JDK 1.4 引入的,旨在使轻松跟踪异常之间的因果关系成为可能。)

【讨论】:

    【解决方案4】:

    承认下面的代码:

    public class MultipleExceptionsExample {
    
       static class IOManip implements Closeable{
           @Override
           public void close() {
               throw new RuntimeException("from IOManip.close");
           }
       }
    
       public static void main(String[] args) {
           try(IOManip ioManip = new IOManip()){
               throw new RuntimeException("from try!");
           }catch(Exception e){
               throw new RuntimeException("from catch!");
           }finally{
               throw new RuntimeException("from finally!");
           }
       }
    }
    

    您将获得所有行:java.lang.RuntimeException: from finally!

    删除finally 块你会得到:java.lang.RuntimeException: from catch!

    删除catch 块你会得到:

    Exception in thread "main" java.lang.RuntimeException: from try!
        Suppressed: java.lang.RuntimeException: from IOManip.close
    

    【讨论】:

      【解决方案5】:

      Suppressed exceptions 是关闭 AutoCloseable 资源时在 try-with-resources 语句 (introduced in Java 7) 中发生的其他异常。由于关闭AutoCloseable 资源时可能会发生多个异常,因此将其他异常附加到primary exception as suppressed exceptions

      查看一段 try-with-resources 示例代码的字节码,标准 JVM exception handlers 用于适应 try-with-resources 语义。

      【讨论】:

        【解决方案6】:

        您也可以在 Java 6 中抑制异常(涉及一些小技巧),

        我创建了一个实用程序,可以透明地处理 Java 1.6 和 Java 1.7 中的抑制异常。你可以找到实现here

        您只需拨打电话:

        public static <T extends Throwable> T suppress(final T t, final Throwable suppressed) 
        

        抑制异常,并且

        public static Throwable [] getSuppressed(final Throwable t) {
        

        获取被抑制的异常异常,以防有人仍在使用 Java 1.6

        【讨论】:

          【解决方案7】:

          ARM - 自动资源管理(从 Java 7 开始引入)

          举个很简单的例子

          static String readFirstLineFromFileWithFinallyBlock(String path)
                                                               throws IOException {
              BufferedReader br = new BufferedReader(new FileReader(path));
              try {
                  return br.readLine();
              } finally {
                  if (br != null) br.close();
              }
          }
          

          现在如果readLine() 函数抛出异常,然后甚至close() 函数[in finally 块] 抛出异常,则后者被赋予更高的优先级并被抛回调用函数。在这种情况下,Exception thrown by the readLine() method is ignored/suppressed。您可以在您的异常中链接导致异常并从 finally 块中重新抛出您的异常。

          由于提供了java 7 功能来检索抑制的异常。您可以在捕获的 throwable 对象上调用public final java.lang.Throwable[] getSuppressed() 函数来查看被抑制的异常。

          例如。

          static String readFirstLineFromFileWithFinallyBlock(String path)
                  throws Exception {
              try (BufferedReader br = new BufferedReader(new FileReader(path));) {
                  return br.readLine();
              }
          }
          

          现在,如果 br.readLine(); 行抛出 Exception1,然后假设在关闭资源时抛出 Exception2 [想象一下这种情况发生在 try-with-resource 语句创建的隐式 finally 块中],那么 Exception1 会抑制 Exception2。

          这里有几点需要注意 -

          1. 如果 try-with-resource 块抛出异常,即在资源实例化时,try 块将不会执行并且会抛出相同的异常。
          2. 如果资源实例化成功,try 块抛出异常,关闭资源时抛出异常,然后关闭资源时抛出的异常被 try 块抛出的异常抑制。
          3. 如果您提供显式 finally 块并且从该块抛出异常,它将抑制所有其他异常。 (此显式 finally 块在资源关闭后执行)

          我已经用代码 sn-ps 编译了大多数可能的场景,并在下面的帖子中输出。

          Suppressed exceptions in java 7

          希望对您有所帮助。

          【讨论】:

          • 在这种情况下,源自 try{} 块的 Exception 不会自动被抑制。程序员可能会选择这样做,而您没有这样做。只有在需要时,try-with-resources 才会自动抑制异常。当他这样做时,您等效的 finally 块中的异常将被抑制。
          • @MartinAndersson 你是对的。当我写答案时有些困惑。我希望编辑后的答案能提供更好的见解。
          【解决方案8】:

          我认为这与“链式异常设施”有关。随着堆栈跟踪的发展,它将影响该设施如何处理异常。随着时间的推移,作为一组链接异常的一部分的异常可以被抑制。查看the Throwable documentation了解更多详情。

          【讨论】:

            猜你喜欢
            • 2011-07-18
            • 1970-01-01
            • 2017-05-19
            • 2011-04-19
            • 2012-03-04
            • 1970-01-01
            • 2019-12-03
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多