为什么允许不引发异常的代码捕获已检查的异常?

在Java中,引发已检查异常(异常或其子类型 - IOException,InterruptedException等)的方法必须声明turps语句:

public abstract int read() throws IOException;

不声明语句的方法不能引发选中的异常。throws

public int read() { // does not compile
    throw new IOException();
}
// Error: unreported exception java.io.IOException; must be caught or declared to be thrown

但是,在安全方法中捕获已检查的异常在java中仍然是合法的:

public void safeMethod() { System.out.println("I'm safe"); }

public void test() { // method guarantees not to throw checked exceptions
    try {
        safeMethod();
    } catch (Exception e) { // catching checked exception java.lang.Exception
        throw e; // so I can throw... a checked Exception?
    }
}

其实不然。这有点滑稽:编译器知道e不是一个检查的异常,并允许重新抛出它。事情甚至有点荒谬,这段代码没有编译:

public void test() { // guarantees not to throw checked exceptions
    try {
        safeMethod();
    } catch (Exception e) {        
        throw (Exception) e; // seriously?
    }
}
// Error: unreported exception java.lang.Exception; must be caught or declared to be thrown

第一个片段是提问的动机。

编译器知道已检查的异常不能在安全方法中引发 - 所以也许它应该只允许捕获未经检查的异常?


回到主要问题 - 是否有任何理由以这种方式实现捕获已检查的异常?这只是设计中的缺陷,还是我错过了一些重要因素 - 也许是后向不兼容?在这种情况下,如果只允许被抓住,可能会出错吗?非常感谢您提供示例。RuntimeException


答案 1

引用 Java 语言规范 §11.2.3:

如果 catch 子句可以捕获已检查的异常类 E1,并且与 catch 子句对应的 try 块不能引发作为 E1 的子类或超类的已检查异常类,则这是编译时错误,除非 E1 是 Exception 或 Exception 的超类。

我猜这个规则早在Java 7之前就已经存在了,当时不存在多重捕获。因此,如果你有一个可以抛出大量异常的块,那么捕获所有内容的最简单方法是捕获一个常见的超类(在最坏的情况下,或者如果你也想捕获s)。tryExceptionThrowableError

请注意,您可能无法捕获与实际抛出的内容完全无关的异常类型 - 在您的示例中,捕获该异常类型的任何子类都不是将是一个错误:ThrowableRuntimeException

try {
    System.out.println("hello");
} catch (IOException e) {  // compilation error
    e.printStackTrace();
}


由OP编辑:答案的主要部分是问题示例仅适用于异常类。通常不允许在代码的随机位置捕获已检查的异常。对不起,如果我使用这些例子混淆了某人。

答案 2

Java 7 引入了更具包容性的异常类型检查

但是,在 Java SE 7 中,您可以在 rethrowException 方法声明的 throw 子句中指定异常类型 FirstException 和 SecondException。Java SE 7 编译器可以确定语句 throw e 引发的异常必须来自 try 块,而 try 块引发的唯一异常可以是 FirstException 和 SecondException。

这段话是在谈论一个专门抛出和的块;即使块抛出,该方法只需要声明它抛出 和 ,而不是:tryFirstExceptionSecondExceptioncatchExceptionFirstExceptionSecondExceptionException

public void rethrowException(String exceptionName)
 throws FirstException, SecondException {
   try {
     // ...
   }
   catch (Exception e) {
     throw e;
   }
 }

这意味着编译器可以检测到唯一可能的异常类型是 s 或 s,这两者都不需要捕获。当你时,它可以告诉,即使静态类型是 ,它不需要被声明或重新捕获。testErrorRuntimeExceptionthrow e;Exception

但是当你把它到 ,这会绕过这个逻辑。现在编译器将其视为需要捕获或声明的普通代码。ExceptionException

将此逻辑添加到编译器的主要原因是允许程序员在重新调用捕获这些特定子类型的常规时,在子句中仅指定特定子类型。但是,在这种情况下,它允许您捕获常规,而不必在子句中声明任何异常,因为可以引发的任何特定类型都不是检查异常。throwsExceptionExceptionthrows