为什么在Java中我们可以捕获Exception
,即使它没有被抛出,但是我们无法捕获它的子类(“unchecked”RuntimeException
和它的子类除外)。示例代码:
class Test {
public static void main(String[] args) {
try {
// do nothing
} catch (Exception e) {
// OK
}
try {
// do nothing
} catch (IOException e) {
// COMPILER ERROR: Unreachable catch block for IOException.
//This exception is never thrown from the try statement body
}
}
}
有什么想法吗?
答案 0 :(得分:25)
任何代码都可能引发RuntimeException
。换句话说,编译器无法轻易预测可以抛出哪种代码。 RuntimeException
块可以捕获catch(Exception e)
。
IOException
是一个经过检查的异常 - 只有声明抛出它的方法调用才能执行此操作。编译器可以(合理地)确信除非有声明抛出它的方法调用,否则不可能发生。
Java编译器根本不考虑“在try块中根本没有代码”的情况 - 它总是允许你捕获未经检查的异常,因为在所有合理的场景中都会有代码哪个可能可能会抛出未经检查的异常。
来自JLS的section 14.21:
如果满足以下两个条件,则可以访问catch块C:
- try块中的某些表达式或throw语句是可以访问的,并且可以抛出一个异常,其类型可以赋予catch子句C的参数。(如果包含它的最内层语句可以访问,则认为表达式是可到达的。)
- 在try语句中没有早期的catch块,因此C的参数类型与A参数类型的子类相同。
可以说编译器应该意识到在你的第一种情况下try块中有 no 表达式......看起来这对我来说仍然是一个无法访问的catch子句。
编辑:正如评论中所述,section 14.20包含以下内容:
如果
catch
子句捕获已检查的异常类型 E1 ,但是没有检查的异常类型 E2 ,那么所有这些都是编译时错误以下举行:
- E2 &lt ;: E1
- 与
try
子句对应的catch
块可以抛出 E2- 紧接着的try语句的前面
catch
块没有捕获 E2 或 E2 的超类型。除非 E1 是异常类。
所以看起来这就是你实际上运行犯规的错误,但是规格并不像14.21中无法到达的捕获区块那样清晰。
答案 1 :(得分:5)
只有在编译器预测代码中可能存在抛出IOException的内容时,才会捕获IO异常。因此,您将收到一条警告,即IO异常永远不会从try语句体中抛出(因为try的正文中没有任何内容)。
答案 2 :(得分:3)
因为对于已检查的异常,抛出它们的方法必须通过'throws'关键字明确说明这个事实,因此如果一个块在你的情况下没有'throws IOException',编译器就会得到它的信息< em>不可能因为抛出了IOException,所以无论你在捕获之后做了什么,它都将无法访问。
答案 3 :(得分:3)
您无法捕获未经检查的异常,因为它们无法被抛出。您可以捕获Exception
,因为未经检查的运行时异常是Exception
并且可能会抛出COULD。
答案 4 :(得分:1)
IOException
是一个经过检查的异常,只能由IO相关代码抛出。由于你的try块什么都不做,所以不会发生IO相关的任何事情,IOExceptions永远不会被抛出,因此catch块无法执行,编译器也不会让你解决它。
正如您所说,Exception可能指的是随时可能发生的未经检查的运行时异常。这是unchecked和checked异常之间的主要区别,这就是编译器不强制执行代码来捕获每个可能的运行时异常的原因。
答案 5 :(得分:0)
Simply Java假设任何代码行都可以抛出通用Exception
或Throwable
,即。 OutOfMemoryException
Error
而非Exception
。同样适用于NPE。
IOException
是一个特殊的异常,只能由托管代码抛出,所以如果你的catch中没有I / O调用阻止你的编译器没有机会捕获它。
只是为了与C#世界进行比较,在C#中这样的代码会被编译但是会出现概念上的错误,因为如果你不做任何事情就不会到达catch块。像ReSharper这样的工具可以警告你。
答案 6 :(得分:0)
如果我们从上面每个人的评论中总结一下,可以得出结论,像 IOException 及其子类这样的完全检查异常意味着由编译器严格检查,并期望相关的 catch 子句抛出。但是对于 RuntimeException 和 Exception 或 Throwable(两者都是部分检查异常,因为它们将 RuntimeException 作为子/孙子)编译器根本无法在编译时说并允许它通过。
所以..
try{
// Empty - valid
} catch(Exception or Throwable or any Runtime Exception ){
}
但是
try{
// Empty - invalid and compile time error
} catch (Any fully checked Exception like IOException, FileNotFoundException, EOF/Interruped etc){
}