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


问题内容

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

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允许在这种情况下被捕获,可能会出什么问题?例子是极大的赞赏。


问题答案:

引用Java语言规范§11.2.3

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

我猜想这个规则起源于Java 7早于Java
7,当时不存在多类别。因此,如果您有一个try可能引发大量异常的块,则捕获所有内容的最简单方法是捕获一个通用的超类(在最坏的情况下Exception,或者Throwable您也想捕获Errors)。

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

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

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