繁体   English   中英

捕获Java中的异常

[英]Catching exceptions in Java

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

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

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

谢谢。

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

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

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

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

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

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

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

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

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

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

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

正如乔恩所说 ,在许多情况下捕捉这些例外很好。 您不应该捕获的异常类型包括NullPointerException和ArrayIndexOutOfBoundsException,这些异常表示代码中存在错误。

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

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

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

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

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

想想这个场景:

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

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

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

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

你可以做任何事情来阻止这种编程,在某些系统中可能会发生操作(在unix中,root用户可能会执行rm -rf / tmp,你无能为力。在windows中我认为系统不会让其他进程删除正在使用的文件)

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

Jon是正确的,有时你无能为力,可能在程序死之前记录,被认为是“处理异常”(可怜的句柄是,但至少处理)

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

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

何时使用其中一个(选中/未选中): 此处

还有其他两种: 这里

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

把这个想法稍微侧重一点:也许在另一个领域它更清楚

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

停止!

所以我们处理异常。

所以回到代码:

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

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

我认为没有第四种选择。

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

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

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM