JavaSE7中抑制的异常

JavaSE7中抑制的异常,java,exception-handling,Java,Exception Handling,我试图理解Java SE7中被抑制的异常,我在下面发布了两个示例,它们很相似,在下面的示例中,我的印象是,当新的“主异常”发生时,被抑制的异常会被忽略,例如,我希望输出为“Java.lang.RuntimeException:y”,但答案是: java.lang.RuntimeException:y 抑制的java.lang.RuntimeException:a 代码如下: class Animal implements AutoCloseable{ @Override pub

我试图理解Java SE7中被抑制的异常,我在下面发布了两个示例,它们很相似,在下面的示例中,我的印象是,当新的“主异常”发生时,被抑制的异常会被忽略,例如,我希望输出为“Java.lang.RuntimeException:y”,但答案是:

java.lang.RuntimeException:y
抑制的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
您的示例

try(Animal a1 = new Animal();){
    foo();
}
catch(Exception e){
    System.err.println(e);
    for(Throwable t : e.getSuppressed()){
        System.err.println("suppressed "+ t);
    }
}
终止,因为
foo()
抛出
运行时异常(
y
)。这就是捕获的目标。因为执行会离开
try
块,所以所有声明的资源都将关闭。关闭
Animal
实例时,会抛出另一个
RuntimeException
a
)。那一个被抑制了,因为它不是根本原因

JLS中解释了将
try with resources
转换为
try catch finally
块的过程

基本的try with resources语句的含义:

try ({VariableModifier} R Identifier = Expression ...)
    Block
{
    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();
            }
        }
    }
}
通过以下对局部变量声明的转换给出 还有一个try-catch-finally语句:

try ({VariableModifier} R Identifier = Expression ...)
    Block
{
    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\u 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);
    }
}
试试看{
最终动物a1=新动物();
可丢弃thr=null;
试一试{
foo();
}捕获(可丢弃的根){
thr=根;
生根;
}最后{
如果(a1!=null){
如果(thr!=null){
试一试{
a1.关闭();
}捕捉(可丢弃抑制){

Oracle文档中的thr.addsupprested(supprested);//:

如果从try块引发异常,并且从try with resources语句引发一个或多个异常,则从try with resources语句引发的异常将被抑制。您可以通过从try块引发的异常调用Throwable.getsupprested方法来检索这些抑制的异常

正如预期的那样,第一个示例给出了输出:

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

这是令人困惑的,因为它混合了try-with-resources和try-with-resources应该能够解决的异常掩蔽行为

另外,您似乎不知道异常被抑制意味着什么。抑制意味着异常被附加到现有异常上,而不是被抛出,并且在过程中导致try块中抛出的异常丢失(通常的术语是“屏蔽”)

异常屏蔽意味着从finally或catch块抛出的异常会导致从try块内抛出的任何异常被丢弃。由于try块中抛出的异常通常描述错误,而close时抛出的异常通常是无趣的,这是一件坏事;请尝试-创造资源是为了减少这一问题的普遍性

因此,在第一个示例中,在try块内的a1上调用foo,在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 exception关闭时抛出的异常没有与之关联的抑制异常,因为它是在退出try with resources块后生成的。

因此,在go()函数的第二个示例中,存在IOException和抑制的“a”异常,然后它执行新的运行时异常“c”,为什么它会丢弃抑制的“a”?@user1529412不是这样做的,你是。你显式地忽略了你捕获的异常,
e
(它有被抑制的异常),并抛出一个全新的异常,
c
。该异常与
try with resources
语句无关。假设我删除了throw new RuntimeException(“c”)行;这意味着我仍然会有附加了“a”的IOException,那么为什么代码什么也不打印?@user1529412:如果您所做的只是删除带有
的行,抛出新的RuntimeException(“c”)
,然后捕获仍然存在。你吃了异常,它消失得无影无踪。好的,是的,这是有意义的。我现在明白了,我把这个例子弄糊涂了,因为我在学习Java证书的东西,他们往往会遇到类似的困惑问题。@user1529412:理解这种事情很好,在现实生活中很有用世界。