在方法中没有抛出声明的情况下抛出检查异常

问题描述

以下代码在 Java 13 上编译和运行:

public class CheckedExceptionSSCE {
    
    public static void main(String[] args) {
        try {
            methodNoThrowsDeclaration();
        } catch (Exception e) {
            // why is this throw allowed?
            // no throws in main()
            throw e;
        }
    }

    private static void methodNoThrowsDeclaration() {
        System.out.println("doesn't throw");
    }
}

为什么允许使用 throw e

它是否在 JLS 中的某处指定?没找到,可能是我用错关键词搜索了。

编译器是否足够聪明来推断不会抛出真正的检查异常,从而允许代码编译和运行?

解决方法

这是 Java 7 中添加的一项功能。 如果您使用 catch 子句中的变量重新抛出异常,编译器可以派生出异常的类型。由于您没有要捕获的检查异常,因此它知道 e 只能是 RuntimeException 并且不需要 throws 定义。

更多信息: https://docs.oracle.com/javase/7/docs/technotes/guides/language/catch-multiple.html

,

方法:

private static void methodNoThrowsDeclaration() {
        System.out.println("doesn't throw");
    }

不抛出任何checked exception,从这个SO thread可以读到:

您可以抛出未经检查的异常,而无需声明它们,如果 你真的很想。未经检查的异常扩展了 RuntimeException。

因此无需修改 main 方法签名。

从 Java 语言规范(§14.18 throw 语句)可以读到:

ThrowStatement:抛出表达式;

至少满足以下三个条件之一,或者 出现编译时错误:

  1. 表达式的类型是未经检查的异常类(第 11.1.1 节) 或 null 类型(第 4.1 节)。

  2. throw 语句包含在 try 语句的 try 块中 (第 14.20 节)并且 try 语句不会抛出 表达式类型的例外。 (在这种情况下,我们说 抛出的值被 try 语句捕获。)

  3. throw 语句包含在方法或构造函数中 声明和表达式的类型可分配(第 5.2 节)到 at 的 throws 子句(第 8.4.6 节、第 8.8.5 节)中列出的至少一种类型 声明。

您显示的代码至少符合第一个条件。但是,让我们看看以下示例,其中上述三个条件都不是 true,即如果您这样做:

private static void methodNoThrowsDeclaration() {
    System.out.println("doesn't throw");
    throw new Exception();
}

那会迫使你这样做:

private static void methodNoThrowsDeclaration() throws Exception {
    System.out.println("doesn't throw");
    throw new Exception();
}

这反过来会导致编译器错误:

Unhandled exception: java.lang.Exception

在语句 throw e; 处。