编译器如何知道在执行之前要抛出的异常?

时间:2013-08-21 13:26:30

标签: java

import java.io.*;
class ex3
{

    public static void main(String args[])
    {
       myfun();
    }

    static void myfun()
    {
        try
        {
           FileInputStream f = new FileInputStream("file.ytxt");
           System.out.println("my fun");
        }

        catch(Exception e) //Line 1
        {
           System.out.println(e.getMessage());
        }

        catch(FileNotFoundException e) //Line 2
        {
           System.out.println("File Not Found Caught");
        }
    }
}

我创建了两个异常处理程序代码(一个是通用的,另一个是第1行和第2行)。

我的编译器在抱怨

ex3.java:24: error: exception FileNotFoundException has already been caught
                catch(FileNotFoundException e)
                ^
1 error

我的问题是编译器是如何知道try块会抛出“FileNotFoundException”的?

10 个答案:

答案 0 :(得分:10)

catch(FileNotFoundException e)此行无法访问。时间:

FileNotFoundException extends Exception

请参阅ExceptionFileNotFoundException

enter image description here

您可能想要切换订单。

答案 1 :(得分:6)

你的问题出在其他地方。 FileNotFoundException将始终捕获Exception,因为这是所有异常的基类。所以编译器基本上抱怨一段死代码

catch(FileNotFoundException e) {
    System.out.println("File Not Found Caught");
}

一般来说,抓住Exception不是一个好主意,你应该总是尝试进行更细粒度的expcetion处理,所以删除它并离开那里FileNotFoundException阻止

答案 2 :(得分:4)

您的编译器可以看到此异常已被前一个catch块捕获。

当您捕获Exception时,它还会捕获任何Exception的子类,例如FileNotFoundException

将catch块的顺序交换为:

    catch(FileNotFoundException e) //Line 2
    {
        System.out.println("File Not Found Caught");
    }
    catch(Exception e) //Line 1
    {
        System.out.println(e.getMessage());
    }

答案 3 :(得分:2)

回答

我的问题是编译器是如何知道try块会抛出“FileNotFoundException”的?

您需要查看FileInputStream

的构造函数
public FileInputStream(String name) throws FileNotFoundException {
    this(name != null ? new File(name) : null);
}

注意它throws FileNotFoundException。此声明告诉编译器可能抛出FileNotFoundException,因此请确保使用try-catch块处理它,或者使用另一个throws子句在方法堆栈上冒泡。

至于你得到的错误,请查看所有其他答案。

答案 4 :(得分:2)

这里有几件事情在发挥作用。

首先,由于FileNotFoundException构造函数的方法签名,编译器知道可能抛出FileInputStream。来自the docs

public FileInputStream(String name) throws FileNotFoundException

因此,签名告诉编译器它可以抛出FileNotFoundException

但是,在这种情况下,由于catch块的排序,该信息实际上并不重要。你有:

catch(Exception e) { /* ... */ }
catch(FileNotFoundException e) { /* ... */ }

catch阻塞工作的方式,如果在try块中抛出异常,则执行遍历每个cacth块并查看{{}中的异常类型1}} block是抛出异常的多态匹配。由于catch来自FileNotFoundException(请参阅here),因此它与第一个块匹配并始终匹配。因此,第二个块是不可达的。

因此,即使编译器知道可以抛出Exception,它仍然可以推断出第二个FileNotFoundException块无法访问,因为任何{{扔掉的东西总是被第一个抓住。

定义catch块时,请始终将它们按最具体的顺序排列,以避免出现此问题。

答案 5 :(得分:1)

改变这个 -

    catch(Exception e) //Line 1
    {
        System.out.println(e.getMessage());
    }

    catch(FileNotFoundException e) //Line 2
    {
        System.out.println("File Not Found Caught");
    }

到此 -

    catch(FileNotFoundException e) //Line 1
    {
        System.out.println(e.getMessage());
    }
    catch(Exception e) //Line 2
    {
        System.out.println("File Not Found Caught");
    }

Exception是Java中所有异常类的超类型。因此,可以将任何异常对象分配给类型为Exception的引用。当程序中发生异常时,将从上到下依次检查catch块,以查找发生的异常类型与catch块正在处理的异常类型之间的匹配。因此,try块中生成的任何类型的异常总是会找到第一个catch块作为匹配项,并且永远无法从您的代码中找到第二个catch块。这正是编译器抱怨的原因。

答案 6 :(得分:1)

catch(Exception e) 
    {
       System.out.println(e.getMessage());
    }

    catch(FileNotFoundException e) 
    {
       System.out.println("File Not Found Caught");
    }

异常是所有异常的超类。

因此,如果我们使用catch(Exception e){},那么它可以捕获所有类型的异常。

因此catch(FileNotFoundExecption f){}将无法访问,因此编译器会给出一个已经被捕获的错误。

这是写它的正确方法:

    catch(FileNotFoundException e) 
    {
       System.out.println("File Not Found Caught");
    }
    catch(Exception e) 
    {
       System.out.println(e.getMessage());
    }

答案 7 :(得分:0)

这不是重点:catch(Exception e)也会抓住任何Exception,即FileNotFoundException。因此,第二次捕获FileNotFoundException是无用的。

先抓住FileNotFoundException,然后抓住Exception

答案 8 :(得分:0)

catch块将拦截指定类及其所有子类的异常。正如其他提到的FileNotFoundExceptionException的子类,所以它将被

捕获
catch (Exception e)

子句。

答案 9 :(得分:0)

Java Virtual Machine Specification: Exceptions

  

Java虚拟机中的每个方法都可以与零或   更多异常处理程序。异常处理程序指定范围   抵消实现该方法的Java虚拟机代码   异常处理程序处于活动状态,描述了异常的类型   异常处理程序能够处理,并指定   处理该异常的代码的位置。例外   如果指令的偏移量匹配异常处理程序   导致异常处于异常的偏移范围内   handler和exception类型是相同的类或子类   异常处理程序处理的异常类。当一个   抛出异常,Java虚拟机会搜索匹配项   当前方法中的异常处理程序。如果是匹配的异常   找到handler,系统分支到异常处理代码   由匹配的处理程序指定。

     

搜索方法的异常处理程序的顺序   比赛很重要。在类文件中,异常处理程序   每个方法都存储在一个表中(§4.7.3)。在运行时,当一个   抛出异常,Java虚拟机搜索异常   当前方法的处理程序按它们出现的顺序排列   类文件中对应的异常处理程序表,从   该表的开头。

如果JDK编译此类代码,则无法访问FileNotFoundException块。由于异常对象是针对instanceof进行检查的,因此它将始终属于第一个异常块。对于您的情况,正确的表将是:

  Exception table:
   from   to  target type
     0    18    21   Class java/io/FileNotFoundException
     0    18    33   Class java/lang/Exception

其中fromto是指令编号。该表格自上而下搜索。