捕获Java中的异常

时间:2009-02-10 23:17:36

标签: java exception exception-handling catch-block

Java中存在某些预定义的异常,如果抛出这些异常,则会报告发生了严重的事情并且您最好改进代码,而不是在catch块中捕获它们(如果我已正确理解它)。但我仍然发现许多程序,其中包含以下内容:

} catch (IOException e) {
     ...
} catch (FileNotFoundException e) {
     ....
}

我认为IOException和FileNotFoundException正是这种异常,我们不应该在catch块中捕获它们。为什么人们这样做?这样抓住他们会更好吗?无论如何,Java编译器都会警告这类问题。

谢谢。

7 个答案:

答案 0 :(得分:14)

不,捕获IOExceptionFileNotFoundException没有任何问题 - 如果你能真正处理那些例外。这是重要的一点 - 你真的可以面对那个例外吗?有时您可以 - 通常在服务器的顶层,例如,因为一个请求失败并不意味着下一个请求无法继续。在客户端应用程序中较少,但它在很大程度上取决于具体情况。当您尝试批量导入时无法读取文件?好的,中止操作但不一定要关闭整个过程...

不可否认,你不应该这样做 - FileNotFoundException会被它所衍生的IOException所掩盖。幸运的是,编译器完全无法阻止你这样做。

答案 1 :(得分:5)

您在IOException之前捕获FileNotFoundException时显示的顺序错误。由于FileNotFoundException扩展IOException,当抛出FileNotFoundException时,将使用第一个处理程序,第二个处理程序是死代码。

我还没有尝试过,但是如果编译的话,我有点惊讶。我希望像FindBugs这样的静态分析工具会捕获这个错误。

至于你是否抓住FileNotFoundException,它取决于来电者。但是,我会说FileNotFoundException通常可以以有意义的方式恢复 - 提示另一个文件,尝试回退位置 - 而不是简单地记录错误或中止该过程。

答案 2 :(得分:4)

Java中有两种类型的异常,已检查异常和未经检查的异常。

必须在catch块中处理已检查的异常。不这样做会导致编译器错误。 IOException是已检查异常的示例,必须进行处理。你实际在这里做的事情取决于有问题的应用程序,但必须处理Exception以保持编译器满意。

不需要捕获未经检查的异常。从RuntimeException扩展的所有类都是未选中的。一个很好的例子是NullPointerException或ArrayIndexOutOfBoundsException。编译器不会强制您捕获这些异常,但它们可能仍会在程序运行时发生,从而导致程序崩溃。

对于记录,可以抛出IOException,就像尝试打开不存在的文件一样简单。处理这样的事情并优雅地恢复是一个好主意(对话框告诉用户文件不存在并重新出现打开的文件对话框),而不是让程序崩溃。

答案 3 :(得分:3)

这样做是为了以不同方式处理不同类型的异常。 通常,您首先要捕获最细粒度的异常,如果在catch块的开头放置更广泛的异常,则首先执行该代码,然后单击finally块。

Jon是对的,捕获IOException的catch将捕获所有IOExceptions和IOException的任何子类型,并且由于FileNotFoundException是一种IOException,它永远不会遇到第二次捕获。

答案 4 :(得分:3)

作为Jon says,在许多情况下捕获这些异常都很好。您不应该捕获的异常类型包括NullPointerException和ArrayIndexOutOfBoundsException,这些异常表示代码中存在错误。

Java有两种类型的异常:已检查异常和未经检查的异常(从RuntimeException继承的异常)。

经过检查的异常(例如IOException)通常用于不可预测的场景,这些场景是编写更好的代码无法避免的。它们被检查的事实意味着编译器强制您编写代码以解决异常情况的可能性。例如,您必须考虑FileNotFoundException的可能性,因为您无法保证该文件将存在(某人可能会在您的程序运行时移动它)。可能会发生IOException,因为网络连接被删除。编译器强制您提供处理这些情况的策略,即使它只是通过允许异常向上传播以调用代码来处理代码来传递负担。

另一方面,未经检查的异常最适用于通过更改代码可以避免的事情。如果代码检查空引用的可能性,则始终可以避免NullPointerException。同样,如果你小心索引,你将永远不会得到ArrayIndexOutOfBoundsException。编译器不会强迫您处理这些场景,因为它们代表了应修复的错误。

答案 5 :(得分:3)

  

我认为IOException和FileNotFoundException正是这种异常

不,这些实际上是“其他”类型的异常,超出了您的编程技能。无论你编程多好,编译器和库都会让你“意识到”可能发生的事情。

想想这个场景:

您创建一个将数据保存到临时文件夹的应用程序。

一切都没问题,你已经检查了文件夹是否存在,如果没有,你可以自己创建它。

然后你要写2 mb到​​那个临时文件夹。

突然,其​​他系统进程,删除你的临时文件夹,你就不能再写了。

在某些系统中可能无法以编程方式阻止此操作,在某些系统中可能会发生操作(在unix中,root用户可能会执行rm -rf / tmp而且你无能为力。在windows中我认为系统不会让其他进程删除正在使用的文件)

通过强制您检查代码中的这种异常,平台设计者认为至少您已经意识到了这一点。

Jon is correct有时你无能为力,可能是在程序死之前记录,被认为是“处理异常”(处理不好,但至少处理)

try { 
   .... 
} catch( IOException ioe ) { 
    logger.severe( 
        String.format("Got ioe while writting file %s. Data was acquired using id = %d, the message is: %s", 
             fileName, 
             idWhereDataCame,
             ioe.getMessage()) );
  throw ioe;
}

您可以做的另一件事是“链接”异常以适应抽象。

可能你的应用程序有一个GUI,向用户显示IOException并不意味着什么,或者可能是security vulnerability。可以发送修改后的消息。

try { 
   .... 
} catch( IOException ioe ) { 
   throw new EndUserException("The operation you've requeste could not be completed, please contact your administrator" , ioe );
}    

并且EndUserException可能被困在gui中的某个地方,并通过Dialog消息呈现给用户(而不是仅仅在没有进一步信息的情况下消失他眼中的应用程序)。当然,你无法做任何事情来恢复IOException,但至少你会死于风格:P

最后,客户端代码可以使用不同的实现,并非所有异常都有意义。

例如,再考虑第一种情况。同样的“操作”可以有三种“插件”服务来执行数据保存。

a) Write the data  to a file.
b) Or, write to a db
c) Or write to a remote server. 

界面不应抛出:

java.io.IOException
java.sql.SQLException 

,也不

java.net.UnknownHostException

但更像是

my.application.DataNotSavedException

并且不同的实现将在正确的级别处理异常,将其转换为适当的抽象:

客户代码:

 DataSaver saver = DataServer.getSaverFor("someKeyIdString");

 try {
     saver.save( myData );
 } catch( DataNotSavedException dnse ) {

     // Oh well... .
     ShowEndUserError("Data could not be saved due to : " dnse.getMessage() );
 }

实施代码:

 class ServerSaver implements DataSaver { 
 ....
     public void save( Data data ) throws DataNotSavedException {
         // Connect the remore server.
         try { 
             Socket socket = new Socket( this.remoteServer, this.remotePort );
             OuputStream out = socket.getOut.... 

             ....
             ....
         } catch ( UnknownHostException uhe ) { 
            // Oops....
            throw new DataNotSavedException( uhe );
         }
    }
 }

FileSaver和DatabaseSaver会做类似的事情。

所有这些都是 Checked Exceptions ,因为编译器会让您检查它们。

何时使用其中一个(已选中/未选中):here

还有其他两种:here

最后对运行时更简单的解释是:here

答案 6 :(得分:0)

稍微提出这个想法:可能在另一个领域更清晰

如果你面前的车突然停了,你会怎么做。

停止!

所以我们处理异常。

回到代码:

如果您需要的文件不可用,您会怎么做?

或者

  1. 有备份。编译成一个 资源,因为它是你的一部分 程序。 我不是在开玩笑。
  2. IFF这是用户提供的文件:告诉用户;这是他们的档案。
  3. 中止程序并向用户发送消息,因为您的软件系统是 碎。
  4. 我认为没有第四种选择。

    我们的C#/ VC ++兄弟选择未经检查的例外。许多“专家”认为检查过的例外是不好的:我的论点是生活很艰难,克服困难。 已检查的异常代表已知的故障模式:必须解决。 您的鱼骨图对于正常操作具有直接的谎言,并且为了失败而从侧面分支。检查异常是预期的失败。

    现在,一旦开始处理运行时异常,它就会变得有趣。 Java程序通常可以使用不起作用的函数正常运行。 通过这个,我的意思是他们抛出空指针异常,数组边界错误,无效参数,并用尽堆空间。这使增量交付非常可行。

    (如果您遇到捕获运行时错误,请记录它们。否则您永远不知道要解决问题)