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

Sim*_*erg 10 java exception throws

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

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

void doSomething() {
    throw new RuntimeException();
}
Run Code Online (Sandbox Code Playgroud)

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

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

额外的信息

以类似的方式,如果doSomething被声明为throw,IOException那么即使被捕获并重新抛出,也只IOException需要在run()-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 ...
}
Run Code Online (Sandbox Code Playgroud)

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

Eug*_*ene 0

我没有按照JLS您在问题中提出的要求进行浏览,因此请对这个答案持保留态度。我想发表评论,但它太大了。


我有时觉得很有趣,javac在某些情况下(就像你的情况)相当“聪明”,但留下了很多其他事情需要稍后处理JIT。在这种情况下,只是编译器“可以告诉”只有 aRuntimeException会被捕获。这是显而易见的,这是你唯一扔进去的东西doSomething。如果您将代码稍微更改为:

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

你会看到一种不同的行为,因为现在javac可以看出你正在抛出一个新的行为Exception,与你捕获的行为无关。

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

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

IMO,因为ex2 = ex;它不应该再次失败,但它确实失败了。

以防万一这是用编译的javac 13+33