首页 文章

异常层次结构/ try-multi-catch

提问于
浏览
1
try {
        throw new FileNotFoundException();
    } catch (IOException e) {
        e.printStackTrace();
    }
    catch (Exception e) {
        e.printStackTrace();
    }

有人能告诉我为什么编译器不会将第二个catch块视为无法访问的代码吗?但在以下情况中:

try {
        throw new FileNotFoundException();
    } catch (Exception e) {
        e.printStackTrace();
    }
    catch (IOException e) {
        e.printStackTrace();
    }

第二个拦截块被认为无法到达?

毕竟,FileNotFoundException属于IOException,就像Exception一样 .

Edit 请澄清:编译器将知道基于方法的方法抛出异常's throws clause. But it may not necessarily know the specific type of exception (under that class of exception). So if a method throws exception ' A ', compiler won'知道实际异常是'A'还是'A'的子类型,因为这只是在运行时确定的 . 然而,编译器将知道永远不会抛出'X'类型的异常,因此为X提供catch块是错误的 . 这是正确的吗?

3 回答

  • 0

    编译器不能假设从 try 块抛出的唯一可能异常将是 FileNotFoundException . 那个's why it doesn't认为你的第一个代码示例中无法访问第二个 catch 块 .

    如果出于某种未知原因,在创建 FileNotFoundException 实例时(完全可能)抛出 RuntimeException 会怎么样?然后怎样呢?

    在您的第一个代码示例中,第二个 catch 块将捕获该意外的运行时异常,而第一个块将在它被抛出时处理 FileNotFoundException .

    但是,在第二个代码示例中,任何和所有异常都会被第一个 catch 块捕获,从而使第二个块无法访问 .

    EDIT:

    为了更好地理解为什么第一个代码中的 catch(Exception e) 块不被编译器视为无法访问,请尝试以下代码,并注意第二个 catch 如何可以访问:

    public class CustomIOException extends IOException {
        public CustomIOException(boolean fail) {
            if (fail) {
                throw new RuntimeException("the compiler will never know about me");
            }
        }
    }
    
    public static void main(String[] args) {
        try {
            throw new CustomIOException(true);
        } catch(IOException e) {
            System.out.println("Caught some IO exception: " + e.getMessage());
        } catch(Exception e) {
            System.out.println("Caught other exception: " + e.getMessage());
        }
    }
    

    输出:

    抓到其他异常:编译器永远不会了解我

  • 3

    First case:

    catch(IOException e){//一个特定的异常
    e.printStackTrace();
    }
    catch(例外e){//如果还有其他例外,请移到此处
    e.printStackTrace();
    }

    如你所见,首先是 IOException . 这意味着我们的目标只是 one 特定的例外 . 然后在第二次捕获中,我们的目标是 IOException 以外的任何其他异常 . 因此它的逻辑性 .

    In second:

    catch(例外e){//无论发生什么异常,都会移动到这里
    e.printStackTrace();
    }
    catch(IOException e){//上面的块已处理*每个异常,因此无法达到 .
    e.printStackTrace();
    }

    我们在第一个块中捕获了 any 异常(无论是 IOException 还是其他一些异常) . 因此,将不会到达第二个块,因为所有内容都已包含在第一个块中 .

    换句话说,在第一种情况下,我们的目标是某些特定的例外,而不是任何其他例外 . 在第二种情况下,我们首先针对所有/任何异常,而不是针对特定异常 . 由于我们已经处理了所有异常,因此稍后具有特定异常将不具有任何逻辑意义 .

  • 2

    TL; DR

    编译器认为 FileNotFoundException() 可能不是唯一抛出的 Exception .


    解释

    JLS§11.2.3 Exception Checking

    如果catch子句可以捕获(第11.2节)已检查的异常类E1并且对应于catch子句的try块可以抛出已检查的异常类E2,E1的子类和前面的catch子句,则鼓励Java编译器发出警告直接封闭的try语句可以捕获已检查的异常类E3,其中E2 <:E3 <:E1 .

    这意味着如果编译器认为catch块可能抛出的唯一异常是 FileNotFoundException() ,它会警告您第二个catch块 . 这不是这种情况 .

    但是,以下代码

    try{
            throw new FileNotFoundException();
        } catch (FileNotFoundException e){
            e.printStackTrace();
        } catch (IOException e){ // The compiler warns that all the Exceptions possibly 
                                 // catched by IOException are already catched even though
                                 // an IOException is not necessarily a FNFException
            e.printStackTrace();
        } catch (Exception e){
            e.printStackTrace();
        }
    

    发生这种情况是因为编译器评估try块以确定可能抛出哪些异常 .

    由于编译器没有在 Èxception e 上警告我们,它认为可能抛出其他异常(例如RunTimeException) . 由于处理这些RunTimeExceptions不是编译器的工作,它让它滑倒 .


    其余的答案是阅读以了解异常捕获背后的机制 .


    架构

    正如您所看到的, Exception 在层次结构中很高,因此必须在层次结构中较低的 IOException 之后最后声明 .


    示例

    想象一下,抛出一个 IOException . 由于它是从 Exception 继承而来的,我们可以说IOException IS-A Exception因此,它总是会被捕获在 Exception 块中,并且 IOException 块将无法访问 .


    真人生活的例子

    比方说,你在商店,必须选择裤子 . 卖家告诉你,你必须尝试从最大的裤子到最小的裤子,如果你找到一个你可以穿的(即使它不是你的尺寸),你必须采取它 .

    你会发现自己买的裤子太大而且你的尺码太大,你也没有机会找到适合自己的裤子 .

    你去另一家商店:那里,你正好相反 . 您可以选择从最小到最大的裤子,如果您找到可以穿的裤子,您必须服用 .

    你会发现自己在买裤子确切的大小 .

    这有点类比,有点奇怪,但它说明了一切 .


    自Java 7:multi-catch

    从Java 7开始,您可以选择将try块中可能抛出的所有类型的异常包含在一个且只有catch块中 .

    警告:您还必须尊重层次结构,但这次是从左到右 .

    在你的情况下,它会

    try{
        //doStuff
    }catch(IOException | Exception e){
        e.printStackTrace();
    }
    

    以下示例(在Java SE 7及更高版本中有效)消除了重复的代码:catch(IOException | SQLException ex){
    logger.log(前);
    抛出前
    }
    catch子句指定块可以处理的异常类型,每个异常类型用竖线(|)分隔 .

相关问题