在本文中,作者介绍了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) {

        // don't 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架构师的道路上披荆斩棘。需要资料的欢迎加入学习交流群:9285,05736

Java9个异常处理的最佳实践相关推荐

  1. .NET中异常处理的最佳实践

    站在巨人的肩膀上,感谢博主将其翻译为中文. 原文地址:http://www.codeproject.com/Articles/9538/Exception-Handling-Best-Practice ...

  2. .NET中异常处理的最佳实践(译)

    原文地址:点击打开链接 本文翻译自CodeProject上的一篇文章,原文地址. 目录 介绍 做最坏的打算 提前检查 不要信任外部数据 可信任的设备:摄像头.鼠标以及键盘 "写操作" ...

  3. 《转载》Java异常处理的10个最佳实践

    本文转载自 ImportNew - 挖坑的张师傅 异常处理在编写健壮的 Java 应用中扮演着非常重要的角色.异常处理并不是功能性需求,它需要优雅地处理任何错误情况,比如资源不可用.非法的输入.nul ...

  4. 【转】Java中关于异常处理的十个最佳实践

    原文地址:http://www.searchsoa.com.cn/showcontent_71960.htm 导读:异常处理是书写强健Java应用的一个重要部分,Java许你创建新的异常,并通过使用 ...

  5. java 异常 最佳实践_关于JAVA异常处理的20个最佳实践

    在我们深入了解异常处理最佳实践的深层概念之前,让我们从一个最重要的概念开始,那就是理解在JAVA中有三种一般类型的可抛类: 检查性异常(checked exceptions).非检查性异常(unche ...

  6. 关于JAVA异常处理的20个最佳实践

    关于JAVA异常处理的20个最佳实践 在我们深入了解异常处理最佳实践的深层概念之前,让我们从一个最重要的概念开始,那就是理解在JAVA中有三种一般类型的可抛类: 检查性异常(checked excep ...

  7. 小菜:Java异常处理的10个最佳实践

    转载自   小菜:Java异常处理的10个最佳实践 异常处理在编写健壮的 Java 应用中扮演着非常重要的角色.异常处理并不是功能性需求,它需要优雅地处理任何错误情况,比如资源不可用.非法的输入.nu ...

  8. 异常处理准则和最佳实践

    让我们回顾一些从对象设计总结的基本异常设计准则:角色,职责和协作(Rebecca Wirfs-Brock和Alan McKean,Addison-Wesley,2003年). 不要尝试处理编码错误. ...

  9. Java 异常处理的 20 个最佳实践,你知道几个?| CSDN 博文精选

    作者 | 武培轩 责编 | 屠敏 出品 | CSDN 博客 异常处理是 Java 开发中的一个重要部分,是为了处理任何错误状况,比如资源不可访问,非法输入,空输入等等.Java 提供了几个异常处理特性 ...

最新文章

  1. 刻意练习:Python基础 -- Task05. 函数与Lambda表达式
  2. Java 8 Lambda
  3. win10磁盘检查命令
  4. 世界五星级大厨经典菜品集
  5. (八)深入浅出TCPIP之TCP长连接与短连接详解
  6. react-native热更新插件react-native-code-push
  7. Javascript:前端利器 之 JSDuck
  8. (转)搭建Spring4.x.x开发环境
  9. UC伯克利造出会「轻功」的机器人,飞檐走壁,一条腿跳遍天下|湾区人工智能...
  10. Linux 系统故障修复和修复技巧
  11. SVN报错之Failed to run the WC DB work queue associated with...的处理方法
  12. java压缩图片大小不改变图片分辨率
  13. 【UE4】 自定义编辑器的放置Actor窗口
  14. 微信小程序canvas实现画圆圈头像
  15. 全志A10/A20 nand flash系统开发
  16. linux命令part,技术|十个鲜为人知的 Linux 命令-Part 3
  17. Go : 与切片类型处理相关的代码(附完整源码)
  18. java类中变量初始化
  19. kong笔记——认识kong
  20. Rosetta Stone罗塞塔:Fatal Application Error #1141

热门文章

  1. 3.1. _3. 覆盖与交换
  2. linux基础知识——mmap
  3. 3.6数对 (Python)
  4. 第一章 计算机网络 3 标准化工作和相关组织 [计算机网络笔记] -简单浏览了解即可
  5. 卧龙吟游戏服务器不显示,卧龙吟你必须知道的隐藏特性
  6. java that关键字_Java中this关键字的几种用法
  7. gentoo linux 分区_小白安装Gentoo Linux操作系统——磁盘分区
  8. wpf 控件生成图片_EyeshotCAD控件生成以及作用
  9. 三宝机器人怎么充电_巨人通力导致吉祥三宝+36号故障怪现象的又一因素
  10. python中set函数作用如何自己用代码实现_Python进阶开发之元类编程