什么是抑制异常?


76

注释(用户SOC上的)回答关于尾调用优化的问题提到了Java 7中有一个称呼,是因为“加ARM的”的“抑制异常”,新的功能(适用于ARM CPU的支持?)。

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


我在“ Java编程语言增强功能”描述download.oracle.com/javase/7/docs/technotes/guides/language/…中
Raedwald

18
ARM意味着自动资源管理,如infoq.com/news/2010/08/arm-blocks
丹尼尔·库尔曼

5
在这种情况下,ARM是try-with-resources的旧名称。他们停止使用ARM,并在Java 7发布之前的某个时间开始使用try-with-resources。@danielkullmann正是ARM所代表的意思
Brad Cupit

Answers:


55

我认为注释者所指的是一个异常,当它finallytry-with-resources块的隐式块中抛出时,在从该try块中抛出现有异常的情况下,它会被半忽略:

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

(这是引用链接页面中名为“抑制的异常”的部分。)



@Raedwald:是的,我想是的。
乔恩·斯基特

6
@JonSkeet @Raedwald:我相信这个答案不能认为Java 7之前存在抑制的异常(我不是在谈论被忽略的异常):如果在该finally块也抛出异常时一个块也抛出了异常try,则来自try区块丢失或“被抑制”(有关更多信息,请参见http://accu.org/index.php/journals/236)。Java 7刚刚添加了一种方便的方法来存储finally块中的异常,因为异常finally是由try-with-resources隐式生成的。
JBert 2011年

5
但要注意的一点是,如果我们在try-with-resource语句中显式提供了finally块,并且抛出了异常,则它优先于try或try-with-resource块抛出的异常。
Aniket Thakur 2014年

63

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

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


1
因此,抑制异常是新功能的结果,这意味着我们在打开和打开文件时不再需要编写繁琐的try...finally子句close()stackoverflow.com/questions/3305405/…–
Raedwald

@JohnB但是我们有Exception(anotherExcetion e)rit的构造函数选项吗?
Kanagavelu Sugumar 2014年

3
@KanagaveluSugumarException(Exception cause)构造函数用于将引起异常的异常包装在另一个更具描述性的异常中。但是,在这种情况下,我们正在谈论没有因果关系的两个不同的例外。异常A不会导致异常B,因此将一个包装在另一个包装中是没有意义的。另外,您假设编码器显式抛出第二个异常并可以访问第一个异常。如果两者都被库调用抛出,则不是这种情况。
约翰B

1
@JohnB我认为您的陈述是错误的。“编译器必须确定要“真正”抛出这些异常。它选择抛出在显式代码(try块中的代码)中引发的异常。”但是编译器将仅选择finally异常,并且将抑制try异常。
Kanagavelu Sugumar 2014年

1
@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
约翰B

21

在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中,try-with-resources; 默认情况下,将AutoCloseable :: close()处的异常与try异常一起添加为抑制的异常。

还应意识到,这不同于链式异常(JDK 1.4引入了该异常,旨在使可以轻松跟踪异常之间的因果关系。)


10

汇编下面的代码:

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


0

您也可以抑制Java 6中的Exception(涉及一些小技巧),

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

您只需要致电:

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

禁止例外,以及

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

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


0

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()函数[infinally块]抛出异常,则后者将被赋予更高的优先级,并被抛回到调用函数中。在这种情况下Exception thrown by the readLine() method is ignored/suppressed。您可以将导致异常的异常链接到异常中,然后从finally块重新抛出异常。

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

例如

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块)

我在下面的文章中使用代码段和输出来编译了大多数可能的情况。

Java 7中的受抑制异常

希望能有所帮助。


1
但是,在这种情况下,Exception不会自动抑制源自try {}块的。程序员可能会选择这样做,而您没有这样做。只有在需要的情况下,try-with-resources才会自动抑制异常。而当他这样做时,您的同等的finally块中的例外将被抑制。
马丁·安德森

1
@MartinAndersson你是对的。我写下答案时有些困惑。我希望编辑后的答案能提供更好的见解。
Aniket Thakur 2014年

-1

我认为这与“链接异常设施”有关。随着堆栈跟踪的发展,它将影响此工具如何处理异常。随着时间的流逝,可以抑制属于一组链接异常的异常。看看该Throwable的文档获取更多细节。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.