java异常最佳处理_处理Java异常的9个最佳实践

本文由程序员新视界原创翻译英文原文链接:https://dzone.com/articles/9-best-practices-to-handle-exceptions-in-java

说起Java异常处理,心情就变得沉重起来。这是一个令初学者流泪,令老手闻之汗颜的话题,因为即使是身经百战的开发者也可能花上几个小时来讨论哪些异常应该抛出或处理,以及如何抛出或处理异常。

这就是为什么大多数开发团队都有自己的一套规则。如果你刚进入一个新的团队,那么你可能会惊讶于这些规则与以前使用过的规则是如此的大相径庭。

不过,经过总结归纳,我们得到了一些大多数团队都会采用的最佳实践。以下是帮助你入门或改进异常处理的9个最重要的实践。

java异常最佳处理_处理Java异常的9个最佳实践_第1张图片

1.在finally块中或使用Try-With-Resource语句清理资源

在try块中使用资源的情况经常发生,例如InputStream,而这在之后是需要关闭的。在这些情况下常见的错误是在try块末尾关闭资源。

java异常最佳处理_处理Java异常的9个最佳实践_第2张图片

问题是,只要没有抛出异常,这种方法似乎工作得很好。try块中的所有语句都将会被执行,且资源也被关闭了。

但是你添加try块事出有因。因为你也许会调用一个或多个可能会抛出异常的方法,或者异常本来就是你自己抛出的。然后你可能就到达不了try块的末尾。作为结果,你将不能关闭资源。

因此,你应该将所有清理好的代码放入finally块中,或者使用try-with-resource语句。

使用finally块

与try块的最后几行相反,finally块总是被执行。情况或者发生在try块成功执行之后或者发生在catch块中处理异常之后。因此,你可以确保清理所有打开的资源。

java异常最佳处理_处理Java异常的9个最佳实践_第3张图片

Java 7的Try-With-Resource声明

另一个选择是try-with-resource语句,我在我的《introduction to Java exception handling》一文中详细解释了这个语句。

如果资源实现AutoCloseable接口,那么你就可以使用它。这正是大多数Java标准资源所做的。当你在try子句中打开资源时,它将会在try块执行后会自动关闭,或者处理异常。

java异常最佳处理_处理Java异常的9个最佳实践_第4张图片

2.倾向于特定异常

你抛出的异常越特殊,越好。一定要记住,遇到一个对你的代码稀里糊涂的同事,那么或许几个月后,你就需要调用方法来处理异常。

因此,确保提供尽可能多的信息给他们。这使得你的API更易于理解。作为结果,你的方法的调用者将能够更好地处理异常,或避免额外的检查。

所以,总是试图找到最适合你的异常事件的类,例如抛出NumberFormatException而不是IllegalArgumentException。并避免抛出非特定异常。

java异常最佳处理_处理Java异常的9个最佳实践_第5张图片

3.记录指定异常

每当你在你的方法签名中指定一个异常时,你也应该在Javadoc中记录它。这与之前的最佳实践具有相同的目标:提供调用者尽可能多的信息,以便他可以避免或处理异常情况。

因此,请确保向Javadoc添加@throws声明并描述可能导致异常的情况。

java异常最佳处理_处理Java异常的9个最佳实践_第6张图片

4.通过描述性消息抛出异常

这个最佳实践背后的想法与前两个类似。但这一次,你不会将信息提供给你的方法的调用者。每个必须了解在日志文件或监视工具中报告异常情况发生了什么的人,都可以读取异常消息。

因此,应该尽可能精确地描述问题,并提供最相关的信息以便理解异常事件。

不要误解我的意思:你可别写成长篇大论。你应该用1-2个短句解释异常原因。这不但有助于你的运营团队了解问题的严重性,还有助于你更轻松地分析任何服务事件。

如果你抛出一个特定异常,它的类名很可能已经描述了错误的类型。因而就不需要提供很多额外的信息。一个很好的例子是NumberFormatException。当你以错误的格式提供String时,它将被java.lang.Long类的构造函数抛出。

java异常最佳处理_处理Java异常的9个最佳实践_第7张图片

NumberFormatException类的名称已经说明了问题。它的消息只需要提供导致问题的输入字符串即可。如果异常类的名称不具有表达性,那么你就需要在消息中提供所需的信息。

17:17:26,386 ERROR TestExceptionHandling:52 - java.lang.NumberFormatException: For input string: "xyz"

5.首先捕获最特定异常

大多数IDE都可以帮助你实现这个最佳实践。当你尝试首先捕获不太特定的异常时,它们会报告无法访问的代码块。

问题是只有匹配异常的第一个catch块被执行。因此,如果首先捕获IllegalArgumentException,那么你将永远不能到达应该处理更特定NumberFormatException的catch块,因为它是IllegalArgumentException的子类。

总是首先捕获最特定异常类,并将不太特定的catch块添加到列表的末尾。

你可以在下面的代码片断中看到这样一个try-catch语句的例子。第一个catch块处理所有的NumberFormatException异常,第二个catch块处理不是NumberFormatException的所有IllegalArgumentException异常。

java异常最佳处理_处理Java异常的9个最佳实践_第8张图片

6.不要捕获Throwable

Throwable是所有异常和错误的超类。你可以在catch子句中使用它,但是你最好永远都不要这么去做!

如果在catch子句中使用Throwable,它不仅会捕获所有异常,它也将捕获所有的错误。JVM抛出的错误,指明不期望由应用程序处理的严重问题。典型例子是OutOfMemoryError或StackOverflowError。这两种情况都是由应用程序无法控制且无法处理的情况引起的。

所以,最好不要捕获Throwable,除非你绝对确定自己处于一种能够或者需要处理错误的异常情况下。

java异常最佳处理_处理Java异常的9个最佳实践_第9张图片

7.不要忽略异常

你曾经分析过一个只有你的用例的第一部分被执行的错误报告吗?

这通常是由于一个被忽略的异常所造成的。开发者可能非常肯定,它永远不会被抛出,并添加了一个不处理也不记录的catch块。当你发现这个块时,你甚至可能会发现里面还夹杂着令人哭笑不得的“This will never happen”注释。

java异常最佳处理_处理Java异常的9个最佳实践_第10张图片

好吧,你可能正在分析一个发生了不可能的问题。

所以,请不要忽视异常。你不知道代码将来会作出怎么样的改变。有人可能会删除阻止异常事件的验证,而没有意识到这会产生问题。或者抛出异常的代码被改变,现在抛出同一个类的多个异常,而调用的代码并不能阻止所有这些异常。

你至少应该写一条日志信息告诉大家,发生了这样不可想象的事情,以及得有人来做一番检查。

java异常最佳处理_处理Java异常的9个最佳实践_第11张图片

8.不要Log和Throw

这可能是这些最佳实践中最常被忽略的一条。你可以找到很多的代码片段,甚至是一些库,里面有异常被捕获,被记录和重新抛出。

java异常最佳处理_处理Java异常的9个最佳实践_第12张图片

在发生异常时记录异常,然后重新抛出异常可能会感觉很直观,可以便于调用者恰如其分地处理异常。但它会为同一个异常写多个错误消息。

17:44:28,945 ERROR TestExceptionHandling:65 - java.lang.NumberFormatException: For input string: "xyz"Exception in thread "main" java.lang.NumberFormatException: For input string: "xyz"at java.lang.NumberFormatException.forInputString(NumberFormatException.java:65)at java.lang.Long.parseLong(Long.java:589)at java.lang.Long.(Long.java:965)at com.stackify.example.TestExceptionHandling.logAndThrowException(TestExceptionHandling.java:63)at com.stackify.example.TestExceptionHandling.main(TestExceptionHandling.java:58)

附加消息也不会添加任何信息。正如最佳实践#4所解释的那样,异常信息应该描述异常事件。堆栈跟踪告诉你在哪个类、哪个方法和哪一行中抛出异常。

如果你需要添加其他信息,则应该捕获异常并将其包装在自定义的信息中。但请务必遵循最佳实践#9。

java异常最佳处理_处理Java异常的9个最佳实践_第13张图片

所以,只有在你想处理异常的时候才去捕获它。否则,请在方法签名中指定它,并让调用者小心它。

9.封装异常

捕获一个标准异常并将其封装到一个自定义异常有时会更好。这种异常的典型例子是应用程序或框架特定业务异常。这允许你添加额外信息,并且你还可以为异常类实施特殊处理。

当你这样做时,确保将原始异常设置为cause。Exception类提供了接受Throwable作为参数的特定构造方法。否则,你将失去原始异常的堆栈跟踪和消息,从而导致难以分析导致异常的异常事件。

java异常最佳处理_处理Java异常的9个最佳实践_第14张图片

总结

正如你所看到的,当你抛出或捕获异常时,你应该考虑到很多不同的事情。这些事情大部分是以提高代码的可读性或API的可用性为目标的。

异常通常同时是错误处理机制和通信媒体。因此,你应该与同事一起讨论着应用最佳实践和规则,以便每个人都能理解通用概念并以相同方式应用这些概念。

你可能感兴趣的:(java异常最佳处理)