首页 > 解决方案 > 捕获异常并重新抛出它,但这不是异常

问题描述

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

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

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

这段代码让我感到惊讶,因为它看起来run()- 方法能够抛出一个Exception,因为它捕获Exception然后重新抛出它,但是该方法没有声明为 throwException并且显然不需要。这段代码编译得很好(至少在 Java 11 中)。

我的期望是我必须在 - 方法中throws Exception声明run()

额外的信息

以类似的方式,如果doSomething声明为 throw IOExceptionthen 只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 ...
}

问题

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

标签: javaexceptionthrows

解决方案


JLS正如您在问题中所问的那样,我没有浏览过,所以请对这个答案持保留态度。我想发表评论,但它太大了。


我有时觉得很有趣,javac在某些情况下(例如在您的情况下)如何非常“聪明”,但还有很多其他事情需要稍后处理JIT。在这种情况下,只是编译器“可以告诉”只有 aRuntimeException会被捕获。这很明显,这是你唯一投入的东西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


推荐阅读