Java SE7中被抑制的异常

问题描述 投票:3回答:3

我试图理解Java SE7中被抑制的Exceptions,我发了2个下面的例子,它们是相似的,在下面的例子中,我的印象是当新的 "main Exception "发生时,被抑制的Exception会被忽略,例如我希望输出是 "java.lang.RuntimeException: y",然而答案是。

java.lang.RuntimeException: y suppressed java.lang.RuntimeException: a

代码是这样的

class Animal implements AutoCloseable{

    @Override
    public void close() {
        throw new RuntimeException("a");   
    }
}

public class ExceptionsDemo {
    public static void main(String[] args) throws IOException {

        try(Animal a1 = new Animal();){
            foo();
        }
        catch(Exception e){
            System.err.println(e);
            for(Throwable t : e.getSuppressed()){
                System.err.println("suppressed "+ t);
            }
        }
    }

    static void foo() {
        try {
            throw new RuntimeException("x");
        } catch (Exception e) {
            throw new RuntimeException("y");
        }    
    }
}

我的理解是,在tryWithResources子句之后,"a "是main Exc,然后在foo()中,x成为main exc,而a被抑制,但在catch中,我想y会成为单独的main exc,并且会忽略所有其他异常,包括被抑制的异常? 就像第二个例子,它就像我刚才说的那样,它输出java.lang.RuntimeException: c,没有抑制的异常。

public class ExceptionDemo2 {

    class Animal implements AutoCloseable{

        @Override
        public void close() {
            throw new RuntimeException("a");   
        }
    }

    public static void main(String[] args) {
        try{
           new ExceptionDemo2().go();
        }
        catch(Exception e){
            System.err.println(e);
            for(Throwable t : e.getSuppressed()){
                System.err.println("suppressed "+ t);
            }
        }
    }

    void go(){
        try(Animal a = new Animal()){
            throw new IOException();
        }catch(Exception e){
            throw new RuntimeException("c");
        }
    }

}

输出了java.lang.RuntimeException: c,没有被抑制的异常。java.lang.RuntimeException: c

java exception-handling
3个回答
5
投票

你的例子

try(Animal a1 = new Animal();){
    foo();
}
catch(Exception e){
    System.err.println(e);
    for(Throwable t : e.getSuppressed()){
        System.err.println("suppressed "+ t);
    }
}

终止,因为 foo() 抛出 RuntimeException (y). 这就是我们的目标。catch. 因为执行离开 try 块,所有声明的资源都被关闭。在关闭 Animal 例如,另一个 RuntimeException (a)被抛出。这个被压制了,因为它不是根本原因。

的翻译是 try-with-resourcestry-catch-finally 块的解释在JLS中。此处.

一个基本的 try-with-resources 语句的含义。

try ({VariableModifier} R Identifier = Expression ...)
    Block

语句的含义是由下面的局部变量声明和 try-catch-finally 语句翻译过来的。

{
    final {VariableModifierNoFinal} R Identifier = Expression;
    Throwable #primaryExc = null;

    try ResourceSpecification_tail
        Block
    catch (Throwable #t) {
        #primaryExc = #t;
        throw #t;
    } finally {
        if (Identifier != null) {
            if (#primaryExc != null) {
                try {
                    Identifier.close();
                } catch (Throwable #suppressedExc) {
                    #primaryExc.addSuppressed(#suppressedExc);
                }
            } else {
                Identifier.close();
            }
        }
    }
}

其中

如果资源规格声明了一个资源,那么 ResourceSpecification_tail 是空的 (而且 try-catch-finally 语句本身不是 try-with-resources 语句)。

你上面的代码基本上翻译成这样

try {
    final Animal a1 = new Animal();
    Throwable thr = null;
    try {
        foo();
    } catch (Throwable root) {
        thr = root;
        throw root;
    } finally {
        if (a1 != null) {
            if (thr != null) {
                try {
                    a1.close();
                } catch (Throwable suppressed) {
                    thr.addSuppressed(suppressed); // <<<<<< suppressing the failure of 'close'
                }
            } else {
                a1.close();
            }
        }
    }
} catch (Exception e) {
    System.err.println(e);
    for (Throwable t : e.getSuppressed()) {
        System.err.println("suppressed " + t);
    }
}

2
投票

这很令人困惑,因为它把 try-with-resources 和异常屏蔽行为混在一起了,而 try-with-resources 本来就是要解决这个问题的。

此外,你似乎不知道异常被抑制的含义。抑制的意思是,异常被附加在一个已有的异常上,而不是被抛出,在进展中导致try-block内抛出的异常丢失(通常的说法是 "屏蔽")。

异常掩盖意味着从final或catch块中抛出的异常会导致任何从try块中抛出的异常被丢弃。由于在 try 块中抛出的异常通常是描述你的错误的,而在关闭时抛出的异常通常是无趣的,这是件坏事;try-with-resources 的创建是为了尝试减少这个问题的发生。

所以在你的第一个例子中,foo是在try块内的a1上调用的,在foo内的catch,y,掩盖了foo的try块中抛出的异常。然后当退出try-with-resources块时,调用close方法,close时抛出的异常会被加到飞行中的y异常上。所以你的printlns显示y,然后迭代附加在y上的被抑制的异常。

在第二个例子中,c是go方法抛出的东西(这和上面描述的屏蔽行为一样)。go方法try块中的IOException被抛出了,在离开的时候调用了close方法,导致close上的异常被加到IOException上,成为一个被压制的异常,然后IOException就被c掩盖了,因为a被掩盖了,而被压制的异常是附加在a上的,所以我们也失去了被压制的异常。关闭时抛出的c异常没有与之相关的压制异常,因为它是在退出try-with-resources块之后产生的。


0
投票

从Oracle文档中可以看到 http:/docs.oracle.comjavasetutorialessentialexceptionstryResourceClose.html。:

如果try块抛出的异常和try-with-resources语句抛出的一个或多个异常,那么try-with-resources语句抛出的这些异常将被抑制。你可以从try块抛出的异常中调用Throwable.getSuppressed方法来检索这些被抑制的异常。

所以正如预期的那样,第一个例子给出了输出。

java.lang.RuntimeException: y
suppressed java.lang.RuntimeException: a

在第二个代码片段中,也有一个异常抑制的情况。为了验证这一点,我已经将你的函数修改为:"我的函数是:"。

void go() {
        try (Animal a = new Animal()) {
            throw new IOException();
        } catch (Exception e) {
            for (Throwable t : e.getSuppressed()) {
                System.err.println("suppressed " + t);
            }
            throw new RuntimeException("c");            
        }
    }

那么输出将是:

suppressed java.lang.RuntimeException: a
java.lang.RuntimeException: c
© www.soinside.com 2019 - 2024. All rights reserved.