你的网站尚未备案 根据,鹿班设计官网,深圳国际红树林中心,wordpress 8211在本文中#xff0c;作者介绍了9个处理异常的最佳方法与实践#xff0c;以举例与代码展示结合的方式#xff0c;让开发者更好的理解这9种方式#xff0c;并指导读者在不同情况下选择不同的异常处理方式。 以下为译文#xff1a;
Java中的异常处理不是一个简单的话题。初学…在本文中作者介绍了9个处理异常的最佳方法与实践以举例与代码展示结合的方式让开发者更好的理解这9种方式并指导读者在不同情况下选择不同的异常处理方式。 以下为译文
Java中的异常处理不是一个简单的话题。初学者很难理解甚至有经验的开发人员也会花几个小时来讨论应该如何抛出或处理这些异常。
这就是为什么大多数开发团队都有自己的异常处理的规则和方法。如果你是一个团队的新手你可能会惊讶于这些方法与你之前使用过的那些方法有多么不同。
然而有几种异常处理的最佳方法被大多数开发团队所使用。下面是帮助改进异常处理的9个最重要的方法。
1. 在Finally中清理资源或者使用Try-With-Resource语句
通常情况下你在try中使用了一个资源比如InputStream之后需要关闭它。在这种情况下一个常见的错误是在try的末尾关闭了资源。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 public void doNotCloseResourceInTry() { FileInputStream inputStream null; try { File file new File(./tmp.txt); inputStream new FileInputStream(file); // use the inputStream to read a file // do NOT do this inputStream.close(); } catch (FileNotFoundException e) { log.error(e); } catch (IOException e) { log.error(e); } }
问题是只要不抛出异常这种方法就可以很好地运行。try内的所有语句都将被执行资源也会被关闭。
但是你在try里调用了一个或多个可能抛出异常的方法或者自己抛出异常。这意味着可能无法到达try的末尾。因此将不会关闭这些资源。
所以应该将清理资源的代码放入Finally中或者使用Try-With-Resource语句。
使用Finally
相比于try无论是在成功执行try里的代码后或是在catch中处理了一个异常后Finally里的内容是一定会被执行的。因此可以确保清理所有已打开的资源。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 public void closeResourceInFinally() { FileInputStream inputStream null; try { File file new File(./tmp.txt); inputStream new FileInputStream(file); // use the inputStream to read a file } catch (FileNotFoundException e) { log.error(e); } finally { if (inputStream ! null) { try { inputStream.close(); } catch (IOException e) { log.error(e); } } } }
Java 7的Try-With-Resource语句
另一个选择是Try-With-Resource语句在introduction to Java exception handling中更详细地说明了这一点。
如果你的资源实现了AutoCloseable接口就可以使用它这正是大多数Java标准资源所做的。当你在try子句中打开资源时它将在try被执行后自动关闭或者处理一个异常。 1 2 3 4 5 6 7 8 9 10 public void automaticallyCloseResource() { File file new File(./tmp.txt); try (FileInputStream inputStream new FileInputStream(file);) { // use the inputStream to read a file } catch (FileNotFoundException e) { log.error(e); } catch (IOException e) { log.error(e); } }
2. 给出准确的异常处理信息
你抛出的异常越具体越好。一定要记住一个不太了解你代码的同事也许几个月后需要调用你的方法并且处理这个异常。
因此请确保提供尽可能多的信息这会使你的API更容易理解。因此你方法的调用者将能够更好地处理异常或者通过额外的检查来避免它。
所以要尽量能更好地描述你的异常处理信息比如用NumberFormatException代替IllegalArgumentException避免抛出一个不具体的异常。 1 2 3 4 5 6 public void doNotDoThis() throws Exception { ... } public void doThis() throws NumberFormatException { ... }
3. 记录你所指定的异常
当你在方法中指定一个异常时你应该在Javadoc中记录下它。这与前面提到的方法有着相同的目标为调用者提供尽可能多的信息这样他们就可以避免异常或者更容易地处理异常。
因此请确保在Javadoc中添加一个throws 声明并描述可能导致的异常情况。 1 2 3 4 5 6 7 8 9 /** * This method does something extremely useful ... * * param input * throws MyBusinessException if ... happens */ public void doSomething(String input) throws MyBusinessException { ... }
4. 使用描述性消息抛出异常
这一最佳实践的理念与前两个相似。但这一次你不用给调用方法的人提供信息。异常消息会被所有人读取同时必须了解在日志文件或监视工具中报告异常时发生了什么。
因此应该尽可能准确地描述问题并提供相关的信息来了解异常事件。
别误会你不需要写一段文字而是应该用1-2个简短的句子解释异常的原因。这可以帮助开发团队理解问题的严重性同时也使你能够更容易地分析任何服务事件。
如果抛出一个特定的异常它的类名很可能已经描述了这种类型的错误。所以你不需要提供很多额外的信息。一个很好的例子就是当你以错误的格式使用字符串时如NumberFormatException它就会被类 java.lang.Long的构造函数抛出。 1 2 3 4 5 try { new Long(xyz); } catch (NumberFormatException e) { log.error(e); }
NumberFormatException已经告诉你问题的类型所以只需要提供导致问题的输入字符串。如果异常类的名称不具有表达性那么就需要提供必要的解释信息。 1 17:17:26,386 ERROR TestExceptionHandling:52 - java.lang.NumberFormatException: For input string: xyz
5. 最先捕获特定的异常
大多数IDE都可以帮助你做到这点当你试图捕获不确定的异常时它会报告一个不可到达的代码块。
问题是只有第一个匹配到异常的catch语句才会被执行所以如果你最先发现IllegalArgumentException你将永远不会到达catch里处理更具体的NumberFormatException因为它是IllegalArgumentException的一个子类。
所以要首先捕获特定的异常类并在末尾添加一些处理不是很具体异常的catch语句。
你可以在下面的代码片段中看到这样一个try-catch语句的示例。第一个catch处理所有NumberFormatExceptions异常第二个catch 处理NumberFormatException异常以外的illegalargumentexception异常。 1 2 3 4 5 6 7 8 9 public void catchMostSpecificExceptionFirst() { try { doSomething(A message); } catch (NumberFormatException e) { log.error(e); } catch (IllegalArgumentException e) { log.error(e) } }
6. 不要在catch中使用Throwable
Throwable是exceptions 和 errors的父类。当然你可以在catch子句中使用它但其实你不应该这样做。
如果你在catch子句中使用Throwable它将不仅捕获所有的异常还会捕获所有错误。JVM会抛出错误这是应用程序不打算处理的严重问题。典型的例子是OutOfMemoryError或StackOverflowError。这两种情况都是由应用程序控制之外的情况引起的无法处理。
所以最好不要在catch中使用Throwable除非你完全确定自己处于一个特殊的情况下并且你需要处理一个错误。 1 2 3 4 5 6 7 public void doNotCatchThrowable() { try { // do something } catch (Throwable t) { // dont do this! } }
7. 不要忽略Exceptions
你是否曾经分析过只有用例的第一部分才被执行的bug报告吗?
这通常是由一个被忽略的异常引起的。开发人员可能非常确信它不会被抛出并添加一个无法处理或无法记录它的catch语句。当你发现它的时候你很可能就会明白一句著名的话“This will never happen”。 1 2 3 4 5 6 7 public void doNotIgnoreExceptions() { try { // do something } catch (NumberFormatException e) { // this will never happen } }
是的你可能在分析一个不可能发生的问题。
所以请千万不要忽略一个例外。你不会知道代码在将来会发生什么变化。有些人可能会删除阻止异常事件的验证而没有意识到这造成了问题。或者抛出异常的代码被更改现在抛出了同一个类的多个异常而调用的代码并不能阻止所有这些异常。
你至少应该写一个日志信息告诉每个人需要检查一下这个问题。 1 2 3 4 5 6 7 public void logAnException() { try { // do something } catch (NumberFormatException e) { log.error(This should never happen: e); } }
8. 不要记录和抛出一个异常
这可能是最常被忽略的。你可以在许多代码片段或者库文件里发现有异常会被捕获、记录和重新抛出。 1 2 3 4 5 6 try { new Long(xyz); } catch (NumberFormatException e) { log.error(e); throw e; }
当它发生时记录一个异常然后重新抛出它以便调用者能够适当地处理它这可能会很直观。但是它会为同一个异常写多个错误消息。 1 2 3 4 5 6 7 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条。 1 2 3 4 5 6 7 public void wrapException(String input) throws MyBusinessException { try { // do something } catch (NumberFormatException e) { throw new MyBusinessException(A message that describes the error., e); } }
因此只需要捕获一个你想要处理的异常在方法中指定它并让调用者处理它。
9. 包装异常
有时最好捕获一个标准异常并将其封装到一个定制的异常中。此类异常的典型例子是应用程序或框架特定的业务异常。这允许你添加额外的信息并且也可以为异常类实现一个特殊的处理。
当你这样做时确保引用原始的异常处理。Exception类提供了一些特定的构造函数方法这些方法可以接受Throwable作为参数。否则你将丢失原始异常的堆栈跟踪和消息这将使你很难分析导致异常的事件。 1 2 3 4 5 6 7 public void wrapException(String input) throws MyBusinessException { try { // do something } catch (NumberFormatException e) { throw new MyBusinessException(A message that describes the error., e); } }
为了让学习变得轻松、高效今天给大家免费分享一套Java入门教学资源。帮助大家在成为Java架构师的道路上披荆斩棘。需要资料的欢迎加入学习交流群928505736