Java无法访问的捕获块编译器错误

Java无法访问的捕获块编译器错误,java,scjp,Java,Scjp,为什么在Java中,我们可以捕获一个异常,即使它没有被抛出,但我们不能捕获它的子类(除了“unchecked”RuntimeExceptions和it子类)。示例代码: class Test { public static void main(String[] args) { try { // do nothing } catch (Exception e) { // OK

为什么在Java中,我们可以捕获一个
异常
,即使它没有被抛出,但我们不能捕获它的子类(除了“unchecked”
RuntimeException
s和it子类)。示例代码:

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
        }       
    }
}

有什么想法吗?

简单地说,Java假设任何代码行都可以抛出一个通用的
异常
可抛出的异常
,即
outofmemory异常
,这是一个
错误
,而不是
异常
。这同样适用于NPE


IOException
是一种只能由托管代码引发的特定异常,因此如果catch块中没有I/O调用,编译器就没有机会捕捉它


与C#world相比,在C#world中,这样的代码将被编译,但这将是一个概念上的错误,因为如果你不做任何事情,你就不会到达catch块。诸如ReSharper之类的工具可以警告您这一点。

任何代码都可能引发
运行时异常。换句话说,编译器无法轻松预测什么样的代码可以抛出它。
catch(异常e)
块可以捕获
RuntimeException

然而,
IOException
是一个选中的异常-只有声明抛出它的方法调用才能这样做。编译器可以(合理地)确信它不可能发生,除非有声明抛出它的方法调用

java编译器根本不考虑“在尝试块中没有任何代码”的情况,它总是允许你捕捉未检查的异常,如在所有合理的情况下,会有可能引发未检查异常的代码。 来自JLS的:

当以下两项均为真时,可到达捕捉块C:

  • try块中的某些表达式或throw语句是可访问的,并且可以引发类型可分配给catch子句C的参数的异常。(如果包含表达式的最内层语句是可访问的,则认为该表达式是可访问的。)
  • try语句中没有较早的catch块A,因此C的参数类型与A的参数类型相同或是A的参数类型的子类
可以说,编译器应该意识到,在第一种情况下,try块中没有表达式。。。在我看来,这仍然是一个不可触及的条款

编辑:如注释中所述,包含以下内容:

如果
catch
子句捕获了选中的异常类型E1,但不存在选中的异常类型E2,则这是一个编译时错误,因此以下所有情况均成立:


  • E2因为对于已检查的异常,抛出它们的方法必须通过'throws'关键字显式声明这一事实,因此,如果一个块在您的情况下没有'throws IOException',编译器就有信息表明IOException不可能被抛出,因此无论您在捕获后做什么,它将是不可访问的。

    您无法捕获未经检查的异常,因为它们无法被抛出。您可以捕获
    异常
    ,因为未经检查的运行时异常是
    异常
    ,可能会引发该异常。

    IOException
    是仅由IO相关代码引发的已检查异常。由于try块不执行任何操作,因此不会发生任何与IO相关的操作,也不会抛出IOExceptions,因此不可能执行catch块,编译器也不会让您处理它。
    正如您所说,异常可能指的是随时可能发生的未经检查的运行时异常。这就是未检查异常和已检查异常之间的主要区别,这就是为什么编译器不强制执行代码来捕获所有可能的运行时异常。

    IO异常只能在编译器预测代码中可能存在引发IOException的内容时被捕获。因此,您将收到一条警告,即决不会从try语句体抛出IO异常(因为try语句体中没有任何内容)。

    如果我们从上面每个人的评论中总结,可以得出结论,像IOException及其子类这样的完全检查的异常意味着要由编译器严格检查,并由相关catch子句抛出。但对于RuntimeException和Exception或Throwable(两者都是部分检查的异常,因为它们都有RuntimeException作为子/孙辈),编译器不能在编译时声明并允许它通过

    所以

    但是


    这意味着没有什么可捕捉的(没有错误)我看不出问题您应该阅读Java语言规范,或者至少是一本好的Java教程。区别在于
    IOException
    (及其所有子类型)都是检查异常,因此编译器可以准确地找出哪些语句可以抛出它们
    Exception
    包括
    RuntimeException
    ,因此可能发生在任何语句中(因为它们未经检查且无需声明)。我认为解决方案是这样的,它明确表示
    Exception
    从“必须抛出”检查中排除:“[…]除非
    E1
    是类
    Exception
    ”@Joachim:找到了。我会在答案中加上这个。
    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){
    
    }