Java9个异常解决的最佳实践
在本文中,作者详情了9个解决异常的最佳方法与实践,以举例与代码展现结合的方式,让开发者更好的了解这9种方式,并指导读者在不同情况下选择不同的异常解决方式。
以下为译文:
Java中的异常解决不是一个简单的话题。初学者很难了解,甚至有经验的开发人员也会花几个小时来探讨应该如何抛出或者解决这些异常。
这就是为什么大多数开发团队都有自己的异常解决的规则和方法。假如你是一个团队的新手,你可能会惊讶于这些方法与你之前使用过的那些方法有多么不同。
然而,有几种异常解决的最佳方法被大多数开发团队所使用。下面是帮助改进异常解决的9个最重要的方法。
1. 在Finally中清除资源或者者使用Try-With-Resource语句
通常情况下,你在try中使用了一个资源,比方InputStream,之后需要关闭它。在这种情况下,一个常见的错误是在try的末尾关闭了资源。
publicvoiddoNotCloseResourceInTry() {
????FileInputStream inputStream = null;
????try{
????????File file = newFile(“./tmp.txt”);
????????inputStream = newFileInputStream(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里的内容是肯定会被执行的。因而,可以确保清除所有已打开的资源。
publicvoidcloseResourceInFinally() {
????FileInputStream inputStream = null;
????try{
????????File file = newFile(“./tmp.txt”);
????????inputStream = newFileInputStream(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被执行后自动关闭,或者者解决一个异常。
publicvoidautomaticallyCloseResource() {
????File file = newFile(“./tmp.txt”);
????try(FileInputStream inputStream = newFileInputStream(file);) {
????????// use the inputStream to read a file
????} catch(FileNotFoundException e) {
????????log.error(e);
????} catch(IOException e) {
????????log.error(e);
????}
}
2. 给出精确的异常解决信息
你抛出的异常越具体越好。肯定要记住,一个不太理解你代码的同事,也许几个月后,需要调用你的方法,并且解决这个异常。
因而,请确保提供尽可能多的信息,这会使你的API更容易了解。因而,你方法的调用者将能够更好地解决异常,或者者通过额外的检查来避免它。
所以,要尽量能更好地形容你的异常解决信息,比方用NumberFormatException代替IllegalArgumentException,避免抛出一个不具体的异常。
publicvoiddoNotDoThis() throwsException {
}
publicvoiddoThis() throwsNumberFormatException {
}
3. 记录你所指定的异常
当你在方法中指定一个异常时,你应该在Javadoc中记录下它。这与前面提到的方法有着相同的目标:为调用者提供尽可能多的信息,这样他们即可以避免异常或者者更容易地解决异常。
因而,请确保在Javadoc中增加一个@throws 公告,并形容可能导致的异常情况。
/**
?* This method does something extremely useful …
?*
?* @param input
?* @throws MyBusinessException if … happens
?*/
publicvoiddoSomething(String input) throwsMyBusinessException {
????…
}
4. 使用形容性消息抛出异常
这一最佳实践的理念与前两个类似。但这一次,你不用给调用方法的人提供信息。异常消息会被所有人读取,同时必需理解在日志文件或者监视工具中报告异常时发生了什么。
因而,应该尽可能精确地形容问题,并提供相关的信息来理解异常事件。
别误解,你不需要写一段文字,而是应该用1-2个简短的句子解释异常的起因。这可以帮助开发团队了解问题的严重性,同时也使你能够更容易地分析任何服务事件。
假如抛出一个特定的异常,它的类名很可能已经形容了这种类型的错误。所以,你不需要提供很多额外的信息。一个很好的例子就是,当你以错误的格式使用字符串时,如NumberFormatException,它就会被类 java.lang.Long的构造函数抛出。
try{
????newLong(“xyz”);
} catch(NumberFormatException e) {
????log.error(e);
}
NumberFormatException已经告诉你问题的类型,所以只要要提供导致问题的输入字符串。假如异常类的名称不具备表达性,那么就需要提供必要的解释信息。
117:17:26,386ERROR TestExceptionHandling:52- java.lang.NumberFormatException: For input string: “xyz”
5. 最先捕获特定的异常
大多数IDE都可以帮助你做到这点,当你试图捕获不确定的异常时,它会报告一个不可到达的代码块。
问题是只有第一个匹配到异常的catch语句才会被执行,所以,假如你最先发现IllegalArgumentException,你将永远不会到达catch里解决更具体的NumberFormatException,由于它是IllegalArgumentException的一个子类。
所以要首先捕获特定的异常类,并在末尾增加少量解决不是很具体异常的catch语句。
你可以在下面的代码片段中看到这样一个try-catch语句的示例。第一个catch解决所有NumberFormatExceptions异常,第二个catch 解决NumberFormatException异常以外的illegalargumentexception异常。
publicvoidcatchMostSpecificExceptionFirst() {
????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,除非你完全确定自己处于一个特殊的情况下,并且你需要解决一个错误。
publicvoiddoNotCatchThrowable() {
????try{
????????// do something
????} catch(Throwable t) {
????????// don’t do this!
????}
}
7. 不要忽略Exceptions
你能否曾经分析过只有用例的第一部分才被执行的bug报告吗?
这通常是由一个被忽略的异常引起的。开发人员可能非常确信它不会被抛出,并增加一个无法解决或者无法记录它的catch语句。当你发现它的时候,你很可能就会明白一句著名的话“This will never happen”。
publicvoiddoNotIgnoreExceptions() {
????try{
????????// do something
????} catch(NumberFormatException e) {
????????// this will never happen
????}
}
是的,你可能在分析一个不可能发生的问题。
所以,请千万不要忽略一个例外。你不会知道代码在将来会发生什么变化。有些人可能会删除阻止异常事件的验证,而没有意识到这造成了问题。或者者抛出异常的代码被更改,现在抛出了同一个类的多个异常,而调用的代码并不能阻止所有这些异常。
你至少应该写一个日志信息,告诉每个人,需要检查一下这个问题。
publicvoidlogAnException() {
????try{
????????// do something
????} catch(NumberFormatException e) {
????????log.error(“This should never happen: “+ e);
????}
}
8. 不要记录和抛出一个异常
这可能是最常被忽略的。你可以在许多代码片段或者者库文件里发现,有异常会被捕获、记录和重新抛出。
try{
????newLong(“xyz”);
} catch(NumberFormatException e) {
????log.error(e);
????throwe;
}
当它发生时记录一个异常,而后重新抛出它,以便调用者能够适当地解决它,这可能会很直观。但是它会为同一个异常写多个错误消息。
17:44:28,945ERROR 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条。
publicvoidwrapException(String input) throwsMyBusinessException {
????try{
????????// do something
????} catch(NumberFormatException e) {
????????thrownewMyBusinessException(“A message that describes the error.”, e);
????}
}
因而,只要要捕获一个你想要解决的异常,在方法中指定它,并让调用者解决它。
9. 包装异常
有时最好捕获一个标准异常并将其封装到一个定制的异常中。此类异常的典型例子是应用程序或者框架特定的业务异常。这允许你增加额外的信息,并且也可以为异常类实现一个特殊的解决。
当你这样做时,确保引用原始的异常解决。Exception类提供了少量特定的构造函数方法,这些方法可以接受Throwable作为参数。否则,你将丢失原始异常的堆栈跟踪和消息,这将使你很难分析导致异常的事件。
publicvoidwrapException(String input) throwsMyBusinessException {
????try{
????????// do something
????} catch(NumberFormatException e) {
????????thrownewMyBusinessException(“A message that describes the error.”, e);
????}
}
为了让学习变得轻松、高效,今天给大家免费分享一套Java入门教学资源。帮助大家在成为Java架构师的道路上披荆斩棘。需要资料的欢迎加入学习交流群:9285,05736
1. 本站所有资源来源于用户上传和网络,如有侵权请邮件联系站长!
2. 分享目的仅供大家学习和交流,您必须在下载后24小时内删除!
3. 不得使用于非法商业用途,不得违反国家法律。否则后果自负!
4. 本站提供的源码、模板、插件等等其他资源,都不包含技术服务请大家谅解!
5. 如有链接无法下载、失效或广告,请联系管理员处理!
6. 本站资源售价只是摆设,本站源码仅提供给会员学习使用!
7. 如遇到加密压缩包,请使用360解压,如遇到无法解压的请联系管理员
开心源码网 » Java9个异常解决的最佳实践