捕获并重新抛出异常,但这不是异常


10

我偶然发现代码看起来像这样:

void run() {
    try {
        doSomething();
    } catch (Exception ex) {
        System.out.println("Error: " + ex);
        throw ex;
    }
}

void doSomething() {
    throw new RuntimeException();
}

这段代码使我感到惊讶,因为它看起来像run()-method能够抛出an Exception,因为它可以捕获Exception然后重新抛出它,但是该方法未声明为throw Exception,显然不需要。这段代码可以很好地编译(至少在Java 11中)。

我的期望是我必须throws Exceptionrun()-method中声明。

额外的信息

以类似的方式,如果doSomething被声明为throw,IOException那么即使被捕获并重新抛出,也只需IOExceptionrun()-method中进行声明Exception

void run() throws IOException {
    try {
        doSomething();
    } catch (Exception ex) {
        System.out.println("Error: " + ex);
        throw ex;
    }
}

void doSomething() throws IOException {
    // ... whatever code you may want ...
}

Java通常喜欢清晰,这种行为背后的原因是什么?一直都是这样吗?Java语言规范中的哪些内容允许run()方法不需要throws Exception在上面的代码段中声明?(如果我要添加它,IntelliJ会警告我Exception永远不要抛出该警告)。


3
有趣。您正在使用什么编译器?如果是IDE编译器,请使用javac-我一直在遇到Eclipse编译器更为宽大的情况。
M. Prokhorov

2
我可以在openjdk-8上重现此行为。值得注意的是,使用该-source 1.6标志进行编译会产生预期的编译错误。使用源兼容性7 进行编译不会引发编译错误
Vogel612 '19

1
自Java 7以来,似乎编译器更智能,并且对可能引发的实际异常进行了更多检查。
michalk

2
这个问题不是重复的,可以在我提供的链接中找到答案In detail, in Java SE 7 and later, when you declare one or more exception types in a catch clause, and rethrow the exception handled by this catch block, the compiler verifies that the type of the rethrown exception meets the following conditions : 1. 1. The try block is able to throw it. 2. There are no other preceding catch blocks that can handle it. 3. It is a subtype or supertype of one of the catch clause's exception parameters.
michalk

2
当前标记重复的肯定是相关的,但不IMO提供足够详细的答案。在该答案的注释中,一个指向JLS 的链接,除此之外没有任何信息。
西蒙·福斯伯格 Simon Forsberg)

Answers:


0

我没有JLS按照您在问题中的要求进行浏览,因此请带一点盐来回答这个问题。我想发表评论,但它太大了。


我有时觉得很有趣,javac在某些情况下(例如您的情况)多么“聪明”,但是还有很多其他事情需要稍后处理JIT。在这种情况下,只是编译器“可以告诉”只有a RuntimeException被捕获。这很明显,这是您投入的唯一内容doSomething。如果您将代码稍微更改为:

void run() {
    try {
        doSomething();
    } catch (Exception ex) {
        Exception ex2 = new Exception();
        System.out.println("Error: " + ex);
        throw ex2;
    }
}

您将看到一种不同的行为,因为现在javac可以告诉Exception您有一个新的抛出,与捕获到的无关。

但是事情远非理想,您可以通过以下方式再次“欺骗”编译器:

void run() {
    try {
        doSomething();
    } catch (Exception ex) {
        Exception ex2 = new Exception();
        ex2 = ex;
        System.out.println("Error: " + ex);
        throw ex2;
    }
}

IMO,因此ex2 = ex;不会再失败,但是会失败。

以防万一这是用 javac 13+33


我在某个链接中读到有人提供的一条信息,即如果您在catch块中重新分配捕获的异常,那么编译器将无法变得聪明。我认为在这种情况下也有类似的情况。编译器知道ex2将引发该异常,该异常最初是作为an创建的,Exception但是随后被重新分配给ex,因此编译器无法精明。
西蒙·佛斯伯格

@SimonForsberg有热情的人JLS可能会来,并提供所需的报价来证明这一点;不幸的是我没有。
尤金(Eugene)

作为记录,当我更改catch块以将捕获的异常重新分配给它自己(ex = ex;)时,不再应用启发式方法。这种现象似乎适用于从7到11甚至是13的所有源级别
Vogel612 '19

看看这个问题,它也是重复的。这和可能的重复项的重复项对此进行了解释,并且还链接到JLS。
michalk
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.