Java Exceptions,要捕获什么以及不应该捕获什么?

时间:2009-11-22 05:18:23

标签: java exception

在运行我的Java应用程序时,我不断遇到可怕的java.something.someException错误。我似乎没有掌握什么例外处理和什么不处理? 当我阅读api文档时,大多数函数会抛出异常,例如,如果我使用I / O或使用数组......等等。

如何根据什么参数决定要捕获的异常和不捕获的异常?

我在这里讨论检查过的例外情况。

8 个答案:

答案 0 :(得分:15)

简短回答

抓住你可以处理的异常,重新抛出你不能做的事情。

答案很长

它被称为异常处理代码的原因是:每当您想要编写catch块时,您需要有充分的理由首先捕获异常。 catch块表示您捕获异常的意图,然后做一些关于它的事情做某事的例子包括但不限于:

  • 重试抛出异常的操作。这在IOException和其他可能是临时的问题(即尝试将文件上传到服务器时出现网络错误)的情况下是有意义的。也许您的代码应重试上载几次)。

  • 记录异常。是的,记录就像做某事一样。您可能还希望在记录之后重新抛出原始异常,以便其他代码仍有机会处理异常,但这取决于具体情况。

  • 在另一个更适合您的类接口的异常中包装异常。例如,如果您有FileUploader类,则可以将IOException包含在更通用的UploadFailedException中,以便使用您的班级的课程不必详细了解您的课程上传代码有效(事实上它抛出IOException在技术上是一个实现细节。)

如果代码在发生问题时无法合理地解决问题,那么你根本不应该抓住它。

不幸的是,这种严厉的规则永远不会在100%的时间内发挥作用。有时,您使用的第三方库会抛出您真正不关心的或者实际上永远不会发生的已检查异常。在这些情况下,您可以使用不运行任何代码的空catch块,但这不是推荐的处理异常的方法。至少,你应该添加一个注释来解释为什么你忽略了异常(但是在评论中作为CPerkins注释,“永远不要说永远不会”。你可能想要实际记录这些类型的“永远不会 - 发生“异常,所以如果发生 这样的异常,你就会意识到这一点,并且可以进一步调查。”

但是,一般规则是,如果您所处的方法无法通过异常做一些合理的事情(记录,重新抛出,重试操作等),那么您不应该编写{{1}完全阻止。让调用方法处理异常。如果您正在处理已检查的异常,请将已检查的异常添加到方法的catch子句中,该子句告诉编译器将异常向上传递给调用方法,这可能更适合处理错误(调用方法可能有更多的上下文,因此它可能更好地了解如何处理异常。)

通常情况下,最好在throws方法中添加try...catch,这将捕获您的代码无法处理的任何异常,并将此信息报告给用户并退出应用程序正常。

最后,不要忘记main

另请注意,即使您没有编写finally块,如果您需要清理代码来运行,您仍可能需要编写catch块,无论是否您尝试执行的操作是否抛出异常。一个常见的例子是在finally块中打开一个文件:即使发生异常,您仍然希望关闭该文件,即使您的方法不会捕获异常。实际上,您可能会在教程和书籍中看到的另一个常见经验法则是try块应该更加常见try...finally阻塞您的代码,正是因为try...catch块应该只是您可以在实际处理异常时编写,但只要您的代码需要自行清理,就需要catch块。

答案 1 :(得分:5)

我强烈推荐Joshua Bloch Effective Java, 2nd Edition中的第9章(例外)来解决这些问题。

答案 2 :(得分:4)

一般的经验法则是处理那些你可以做些什么但不处理你不能做的事情。在您不处理异常的情况下,调用者应该处理它们。如果您正在编写框架或库,通常最终会在库或特定于框架的异常中包含诸如SQLException之类的低级异常,以抽象出较低级别的详细信息。

例如,如果您正在编写一个写入磁盘上文件的方法,那么您应该处理FileNotFoundException,因为您可以创建丢失的文件,但是如果您遇到创建文件或写入的问题然后你应该让调用者处理它(在执行任何需要完成的清理工作之后)。

答案 3 :(得分:3)

这是我个人的发现:

  • 您需要在主例程中尝试{} catch(Throwable o){...},以便捕获,记录和告知用户任何意外异常。
  • 检查已检查的异常是因为您需要作为程序员来决定在发生时做什么。请记住,一个决定可能只是说“好的,崩溃的时间”。
  • 如果你最终遇到一个带有检查异常的致命情况,你可以做的只是崩溃,那么“抛出新的RuntimeException(”reason“,checkedException);”所以上面的处理程序有记录的东西。包括重要局部变量的值 - 记住有一天你必须调试一个情况,你唯一拥有的就是堆栈跟踪。
  • 永远不要抓住异常而忽略它。如果您拥有,则必须记录为什么允许您违反规则,并在catch块中执行此操作。

有一天会有所帮助的提示:当你崩溃时,提供一种简单的方法让用户看到邮件通过电子邮件发送堆栈跟踪给你。


编辑:如果可用的例外情况不能完全覆盖您的需要,请不要害怕创建新例外。这允许您在堆栈跟踪中获得更好的命名,并且您的错误处理代码可以轻松区分不同的情况。您可能希望将所有自己的异常基于公共基类(“OurDomainException”),这样您就可以使用catch子句中的基类来查看它是什么类型。

答案 4 :(得分:2)

用Java编写了几年之后,我同意通过编写无限的try-catch块很容易让人烦恼。

快速编码的一种好方法是尽可能低地捕获特定异常,并在最外层(Exception)捕获基础main(),以便您可以编写通用错误消息,而不是让程序崩溃。

这使您可以非常快速地运行代码,然后您可以花时间在各个层中使用其处理逻辑添加特定异常。

答案 5 :(得分:2)

已检查Catch Exception,请勿抓取RuntimeException。尝试捕获特定的异常,尽量不要捕获泛型java.lang.Exception

答案 6 :(得分:1)

模块边界

我也捕获了更清洁模块边界的例外情况。例如,如果抛出了一个我无法处理的SQLException,我会抓住它并抛出我自己的描述性异常(将SQLException作为原因)。这样调用者不必知道我正在使用数据库来满足他的请求。他只是收到错误“无法处理此特定用例”。如果我决定在没有数据库访问的情况下完成他的请求,我就不必更改我的API。

答案 7 :(得分:0)

根据经验,我会捕捉异常,然后我可以用它做什么,或者我希望异常停止向上移动。例如,如果我正在处理项目列表,并且我希望即使其他项目失败也要处理下一个项目,那么我将围绕该项目处理放置一个try ... catch块并且仅在那里。我通常在我的程序的主要方法中尝试一下try(Throwable),这样我就可以记录错误,比如找不到的类或类似的东西。

如果我不知道如何处理该异常,我不会在方法中放置try ... catch块。如果这样做,您将使用大量异常处理代码来膨胀您的代码。