Java SE7中禁止的异常

时间:2015-12-17 16:44:36

标签: java exception-handling

我试图了解Java SE7中的抑制异常,我在下面发布了2个示例,它们是相似的,在下面的示例中,我的印象是当新的"主要异常"发生了,被抑制的被忽略了,例如我期望输出是" 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"是主要的Exc,然后在foo()中,x变为主要的exc而a被压制,但是在catch中,我认为y将成为独奏主要的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

3 个答案:

答案 0 :(得分:4)

你的例子

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

终止,因为foo()会引发RuntimeExceptiony)。这是catch的目标。由于执行离开try块,因此所有声明资源都将关闭。关闭Animal实例时,会抛出另一个RuntimeExceptiona)。那个被压制,因为它不是根本原因。

JLS here解释了try-with-resourcestry-catch-finally块的转换。

  

基本的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 {
    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);
    }
}

答案 1 :(得分:2)

这是令人困惑的,因为它将try-with-resources与资源试图解决的异常屏蔽行为混合在一起。

此外,您似乎还没有意识到要禁止异常的含义。抑制意味着异常被添加到现有异常,而不是被抛出,并且在进程中导致try-block中抛出的异常丢失(通常的术语被“屏蔽”)。

异常屏蔽意味着从finally或catch块抛出的异常导致try块中抛出的任何异常被丢弃。由于try-blocks中抛出的异常通常描述了你的错误,并且关闭时抛出的异常通常是无趣的,这是一件坏事;创建了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,我们也失去了被抑制的异常。关闭时抛出的c异常没有与之关联的抑制异常,因为它是在退出try-with-resources块之后生成的。

答案 2 :(得分:0)

来自Oracle文档http://docs.oracle.com/javase/tutorial/essential/exceptions/tryResourceClose.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