在Java中,抛出 checked 异常(Exception或其子类型 - IOException,InterruptedException等)的方法必须声明 throws 语句:
public abstract int read() throws IOException;
不声明 throws
语句 can't 抛出已检查异常的方法 .
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
第一个片段是一个问题的动机 .
编译器知道检查的异常不能被抛出一个安全的方法 - 所以也许它应该只允许捕获未经检查的异常?
回到 main question - 有没有理由以这种方式实现捕获检查的异常?这只是设计中的缺陷还是我遗漏了一些重要因素 - 可能是后向不兼容?如果在这种情况下只允许 RuntimeException
被捕获,可能会出现什么问题?非常感谢例子 .
3 回答
Java 7引入了more inclusive exception type checking .
这段话是关于
try
块特别抛出FirstException
和SecondException
;即使catch
块抛出Exception
,该方法只需要声明它抛出FirstException
和SecondException
,而不是Exception
:这意味着编译器可以检测到
test
中抛出的唯一可能的异常类型是Error
s或RuntimeException
s,这两种类型都不需要被捕获 . 当你throw e;
时,它可以告诉,即使静态类型是Exception
,它也不需要声明或重新捕获 .但当你把它投射到
Exception
时,这会绕过那个逻辑 . 现在,编译器将其视为需要捕获或声明的普通Exception
.将此逻辑添加到编译器的主要原因是允许程序员在重新抛出捕获这些特定子类型的常规
Exception
时仅在throws
子句中指定特定的子类型 . 但是,在这种情况下,它允许您捕获一般Exception
而不必在throws
子句中声明任何异常,因为没有可以抛出的特定类型是已检查的异常 .这里的问题是检查/未检查的异常限制会影响允许您抛出代码的内容,而不会影响允许捕获的内容 . 虽然你仍然可以捕获任何类型的
Exception
,但是你唯一允许实际投掷的是未经检查的那些 . (这就是为什么将未经检查的异常转换为已检查的异常会破坏您的代码的原因 . )使用
Exception
捕获未经检查的异常是有效的,因为未经检查的异常(a.k.a.RuntimeException
s)是Exception的子类,它遵循标准多态规则;它不会将捕获的异常变为Exception
,就像在Object
中存储String
不会将String
变为Object
一样 . 多态性意味着可以容纳Object
的变量可以包含从Object
派生的任何内容(例如String
) . 同样,由于Exception
是所有异常类型的超类,因此Exception
类型的变量可以包含从Exception
派生的任何类,而不会将对象转换为Exception
. 考虑一下:尽管变量的类型是
Object
,o
仍然存储String
,不是吗?同样,在您的代码中:这意味着实际上“捕获任何与类
Exception
兼容的东西(即Exception
以及从它衍生的任何东西) . ”类似的逻辑也用于其他语言;例如,在C中,捕获std::exception
也会捕获std::runtime_error
,std::logic_error
,std::bad_alloc
,任何正确定义的用户创建的异常,依此类推,因为它们都来自std::exception
.tl; dr:您没有捕获已检查的异常,您正在捕获任何异常 . 如果将异常强制转换为已检查的异常类型,则该异常仅成为已检查的异常 .
引用Java Language Specification, §11.2.3:
我猜这个规则早在Java 7之前就已经存在了,而Java 7并不存在多重捕获 . 因此,如果你有一个
try
块可能抛出大量异常,捕获所有内容的最简单方法是捕获一个共同的超类(在最坏的情况下,Exception
,或Throwable
,如果你想捕获Error
也是如此) .请注意,您可能无法捕获与实际抛出的内容完全无关的异常类型 - 在您的示例中,捕获
Throwable
的任何子类Throwable
将是一个错误:由OP编辑:答案的主要部分是问题示例仅适用于Exception类 . 通常,在代码的随机位置不允许捕获已检查的异常 . 对不起,如果我混淆了使用这些例子的人 .