Java 异常处理最佳实践 ╰半夏微凉° 2022-04-18 06:47 273阅读 0赞 ### 1. 在**Finally**语句块中释放资源或者使用**Try-With-Resource**语句 ### 比如,在**Try**语句中使用**InputStream**输入流,并且试图在**Try**语句块中关闭资源,比如下面的代码就不是推荐做法。 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); } } 正确做法是在**Finally**语句块中执行资源释放操作,比如,下面的代码就是推荐做法: 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**语句,如果资源实现了[AutoCloseable][],资源将自动释放。 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. 指定异常而非通用异常 ### 最佳实践:在抛出异常时,最好异常应尽可能的准确,现实中你不可能一个人单打独斗,你需要与他人进行合作、交互、甚至联调,对于一个陌生人抑或甚至你团队中的其他人,他们也不大可能熟悉你内部的代码逻辑或者你本人,那么你的代码逻辑所抛出的异常应尽可能的准确以便他们可以正确处理。所以,推荐做法是尽可能的告知对方可能需要知道的准确信息,接口应能准确的表达实际含义,这样的话,方法调用方才可以很好的处理异常或者通过[额外检查][Link 1]来避免这种异常。所以,应尽可能的抛出符合实际的异常信息,比如:抛出[NumberFormatException][]异常而非 [IllegalArgumentException][],同时应避免代码中直接抛出**Exception**异常。 public void doNotDoThis() throws Exception { ... } public void doThis() throws NumberFormatException { ... } ### 3. 应对抛出的异常作必要的说明 ### 如果方法抛出异常,那么Javadoc中最好给出有关这个异常的说明,理由与最佳实践2一样,就是给方法调用者尽可能多的信息,以便调用方可以处理或者避免该异常。 /** * This method does something extremely useful ... * * @param input * @throws MyBusinessException if ... happens */ public void doSomething(String input) throws MyBusinessException { ... } ### 4. 抛出的异常应包含足够的描述性的信息 ### 该最佳实践的理念与前两者类似,但与前两者不同的是,该最佳实践并不调用方方法信息,此最佳实践的目标在于当异常发生时,异常关联方可以通过异常信息来获知问题所在,因此,异常中所包含的描述信息应尽可能准确,但,千万不能误解此实践含义,尽可能准确的意思并不是说你需要一大段的描述来说明异常,只需要简单的一两句话说明异常发生的原因,以便运维同事可以快速定位问题原因,同时也能帮助你来确认服务事故。 如果抛出特定类型异常,异常的名称最好是能描述异常的种类或者类型,这样做的目的是避免其他不必要的额外信息,比如我们常见的**NumberFormatException**就是一个很好的例子。 try { new Long("xyz"); } catch (NumberFormatException e) { log.error(e); } 抛出的异常完全可以说明问题原因,因而,你根本无需其他额外补充信息。 17:17:26,386 ERROR TestExceptionHandling:52 - java.lang.NumberFormatException: For input string: "xyz" ### 5. 首先捕获最准确异常 ### 如今,绝大部分**IDEs**都可以做到这一点,也就是说,如果由多个异常捕获语句,那么最里面的异常应是最“精确”的异常,异常捕获应从“小”到“大”,从“精确”到“模糊”。如果,你首先捕获范围最大的那个异常,那么**IDE**会告诉你,其余异常将**不可达**:`unreachable`,比如,下面的代码段中,首先处理的是`NumberFormatException`。 public void catchMostSpecificExceptionFirst() { try { doSomething("A message"); } catch (NumberFormatException e) { log.error(e); } catch (IllegalArgumentException e) { log.error(e) } } ### 6. 不要使用`Throwable` ### [`Throwable`][Throwable]是所有异常和错误的父类,你可以在`catch`语句中定义使用,但建议不要这么做。一旦你这么做,不仅代码不仅会捕获所有异常,而且同样也会捕获所有的错误,比如由JVM抛出的,不应由程序本身来处理的错误都会被程序“吃”掉,例如,我们常见的[OutOfMemoryError][]和[ StackOverflowError][StackOverflowError],当发生这种类型的异常时,错误已经超过应用本身可控范围。 public void doNotCatchThrowable() { try { // do something } catch (Throwable t) { // don't do this! } } ### 7. 不要忽略任何异常 ### 你是否遇见过这种情况,用例刚开始执行就出现BUG?这种错误通常是由“被忽略的”异常所导致的,可能在代码最开始的时候,开发人员笃信某段代码不会发生异常,所以使用了一个catch语句,但语句块中没有任何异常处理逻辑或者日志记录,即便当你去**REVIEW**代码,你也会发现类似这种的注释: > *This will never happen* public void doNotIgnoreExceptions() { try { // do something } catch (NumberFormatException e) { // this will never happen } } 但,代码可能在不断的演变,你可能无法知道未来代码所处的上下文会变成什么样子,比如某天项目组的某个人删除了某些校验条件,原本不会抛出任何异常的代码可能会抛出多种异常,所以正确的做法,至少你应该以日志记录的方式告诉大家,未预期的异常发生了,这里需要有人去校验。 public void logAnException() { try { // do something } catch (NumberFormatException e) { log.error("This should never happen: " + e); } } ### 8. 不要同时记录然后再抛出异常 ### 这可能是我们最容易忽略的编码实践,可能你会经常看到过类似的代码段,甚至在某些Java库中也有类似的编码习惯,异常被捕获,然后记录,然后在重新抛出。 try { new Long("xyz"); } catch (NumberFormatException e) { log.error(e); throw e; } 对于方法调用方来说,当异常发生时,可以很方便的通过日志记录来分析异常,但这种做法带来的问题是针对同一个错误重复记录多条错误信息,而且多余的信息并没有提供其他额外有用的信息。 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. 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`类作为参数,否则的话,自定义封装将隐藏原始错误信息,最初也会导致异常无法正确分析。 public void wrapException(String input) throws MyBusinessException { try { // do something } catch (NumberFormatException e) { throw new MyBusinessException("A message that describes the error.", e); } } *原文链接* [https://dzone.com/articles/9-best-practices-to-handle-exceptions-in-java][https_dzone.com_articles_9-best-practices-to-handle-exceptions-in-java] [AutoCloseable]: https://docs.oracle.com/javase/8/docs/api/java/lang/AutoCloseable.html [Link 1]: https://stackify.com/top-java-software-errors/ [NumberFormatException]: https://docs.oracle.com/javase/8/docs/api/java/lang/NumberFormatException.html [IllegalArgumentException]: https://docs.oracle.com/javase/8/docs/api/java/lang/IllegalArgumentException.html [Throwable]: https://docs.oracle.com/javase/8/docs/api/java/lang/Throwable.html [OutOfMemoryError]: https://docs.oracle.com/javase/8/docs/api/java/lang/OutOfMemoryError.html [StackOverflowError]: https://docs.oracle.com/javase/8/docs/api/java/lang/StackOverflowError.html [https_dzone.com_articles_9-best-practices-to-handle-exceptions-in-java]: https://dzone.com/articles/9-best-practices-to-handle-exceptions-in-java
相关 Java异常处理最佳实践实例 在Java编程中,异常处理是确保程序稳定运行的重要机制。以下是一个Java异常处理的最佳实践实例: ```java public class BankAccount { 迈不过友情╰/ 2024年09月18日 12:51/ 0 赞/ 6 阅读
相关 Java异常处理的最佳实践 在Java中,异常处理是保证程序健壮性的重要手段。以下是一些关于Java异常处理的最佳实践: 1. **使用try-catch-finally**:当可能产生异常的代码块存在 「爱情、让人受尽委屈。」/ 2024年09月12日 19:36/ 0 赞/ 28 阅读
相关 异常处理的最佳实践:Java异常处理案例对比 在Java编程中,异常处理是一种机制,用于在程序运行过程中遇到问题时进行处理。以下是一些关于异常处理的最佳实践以及实际案例的对比: 1. **声明并捕获异常**: - 柔情只为你懂/ 2024年09月10日 10:36/ 0 赞/ 34 阅读
相关 Java异常处理的最佳实践 在Java中,良好的异常处理实践有助于程序的稳定性和可读性。以下是一些最佳实践: 1. **使用try-catch-finally结构**: - 将可能抛出异常的代码放 你的名字/ 2024年09月05日 20:00/ 0 赞/ 36 阅读
相关 Java中异常处理的最佳实践 在Java中,异常处理是程序设计中防止错误影响系统正常运行的重要手段。以下是一些关于Java中异常处理的最佳实践: 1. **捕获具体异常**:尽量捕获你能够处理的特定异常类 阳光穿透心脏的1/2处/ 2024年09月05日 19:36/ 0 赞/ 19 阅读
相关 java异常最佳处理_Java异常处理的最佳实践 本文是关于 Exception 处理的一篇不错的文章,从 Java Exception 的概念介绍起,依次讲解了 Exception 的类型(Checked/Unchecked 雨点打透心脏的1/2处/ 2022年11月08日 15:27/ 0 赞/ 175 阅读
相关 Java 异常处理最佳实践 1. 在Finally语句块中释放资源或者使用Try-With-Resource语句 比如,在Try语句中使用InputStream输入流,并且试图在Try语句块中关闭资 ╰半夏微凉°/ 2022年04月18日 06:47/ 0 赞/ 274 阅读
相关 Java异常处理最佳实践 1. 在 finally 块中清理资源或者使用 try-with-resource 语句 ### 当使用类似InputStream这种需要使用后关闭的资源时,一个常见的错... 灰太狼/ 2020年06月18日 09:09/ 0 赞/ 853 阅读
还没有评论,来说两句吧...