4

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

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

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

谢谢你。

4

7 回答 7

14

IOException不,捕捉和FileNotFoundException- 如果你能真正处理这些异常并没有错。这是重要的一点——面对那个例外,你真的能继续吗?有时你可以——通常是在服务器的顶层,例如,仅仅因为一个请求失败并不意味着下一个请求不能继续。在客户端应用程序中不太常见,尽管这在很大程度上取决于情况。尝试批量导入时无法读取文件?好的,中止操作,但不一定要关闭整个过程...

诚然,你不应该这样使用它们——它FileNotFoundException会被IOException它的来源所掩盖。幸运的是,编译器完全阻止您这样做。

于 2009-02-10T23:19:35.280 回答
5

你显示的顺序,IOException之前被抓住FileNotFoundException是错误的。由于FileNotFoundExceptionextends IOException,当 aFileNotFoundException被抛出时,将使用第一个处理程序,第二个处理程序是死代码。

我还没有尝试过,但是如果编译成功,我会感到有些惊讶。我希望像 FindBugs 这样的静态分析工具会捕捉到这个错误。

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

于 2009-02-10T23:30:01.183 回答
4

Java中有两种异常,检查异常和未检查异常。

已检查的异常必须在 catch 块中处理。不这样做会导致编译器错误。IOException 是已检查异常的示例,必须进行处理。您在此处实际执行的操作取决于相关应用程序,但必须处理异常以使编译器满意。

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

作为记录,IOException 可以被抛出,比如试图打开一个不存在的文件。处理这样的事情并优雅地恢复是一个好主意(向用户发出对话框说文件不存在并重新出现打开的文件对话框或其他东西),而不是让程序崩溃。

于 2009-02-10T23:32:34.810 回答
3

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

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

于 2009-02-10T23:20:32.797 回答
3

正如Jon 所说,在许多情况下捕获这些异常是可以的。您不应该捕获的异常类型是 NullPointerException 和 ArrayIndexOutOfBoundsException,它们表明您的代码中存在错误。

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

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

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

于 2009-02-10T23:51:15.640 回答
3

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

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

想想这个场景:

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

一切正常,您已检查该文件夹是否存在,如果不存在,则您自己创建它。

然后您将 2 MB 写入该临时文件夹。

突然,其他系统进程删除了您的临时文件夹,您无法再写入。

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

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

乔恩是正确的,有时你无能为力,可能在程序死亡之前记录,这被认为是“处理异常”(糟糕的处理是的,但至少处理)

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 没有任何意义,也可能是一个安全漏洞。可以发送修改后的消息。

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 会做类似的事情。

所有这些都是检查异常,因为编译器会让你检查它们。

何时使用其中一个(选中/未选中):这里

还有另外两种:这里

最后,运行时的一个更简单的解释是:这里

于 2009-02-11T00:29:45.173 回答
0

稍微考虑一下这个想法:也许在另一个领域会更清楚

如果前面的车突然停下来怎么办。

停止!

所以我们处理异常。

所以回到代码:

如果您需要的文件不可用怎么办?

任何一个

  1. 有备份。编译为资源,因为它是您程序的一部分。我不是在开玩笑。
  2. IFF 这是用户提供的文件:告诉用户;这是他们的档案。
  3. 由于您的软件系统已损坏,因此中止程序并向用户发送消息。

我认为没有第四种选择。

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

现在,一旦您开始处理运行时异常,它就会变得有趣。Java 程序可以在大部分情况下正常运行,但具有不起作用的功能。我的意思是,它们抛出空指针异常、数组边界错误、无效参数以及堆空间不足。这使得增量交付非常可行。

(如果您确实捕获了运行时错误,请将它们记录下来。否则您永远不知道要修复问题)

于 2009-02-11T00:51:12.237 回答