关于Java异常处理的最佳实践,你了解多少?

出品 | CSDN博客

异常处理是 Java 开发的重要组成部分,用于处理任何错误情况,例如资源不可访问、非法输入、空输入等。Java 以 try、catch 和 finally 关键字的形式提供了几种内置于语言本身的异常处理功能。Java 编程语言还允许创建新的自定义异常并使用 throw 和 throws 关键字抛出它们。在Java编程中,Java的异常处理不仅仅是知道语法那么简单,它必须遵循标准的JDK库,以及处理错误和异常的开源代码。

在这里,我们将讨论一些 Java 异常处理的最佳实践。在讨论异常处理的最佳实践之前,让我们先了解几个重要的概念,即什么是异常及其分类。

什么是例外?

异常的英文单词是exception,本质上是程序错误,包括程序逻辑错误和系统错误。比如使用空引用、越界数组下标、内存溢出错误等等,这些都是偏离我们程序本身意图的意外情况。在编写程序的过程中经常会出现错误,包括编译和运行时的错误。编译期间发生的错误由编译器纠正。但是,运行时的错误不在编译器的能力范围内,在运行时也是如此。错误通常是不可预测的。如果程序运行过程中出现错误,如果被忽略,程序就会终止或者直接导致系统崩溃,这显然不是我们想要看到的结果。

操作过程中出现的错误如何处理和补救?Java提供了一种异常机制,通过异常机制来处理程序执行过程中发生的错误。通过异常机制,我们可以更好的提高程序的健壮性。

异常分类

Java 将异常视为对象,并将基类 java.lang.Throwable 定义为所有异常的超类。

Java 包括三种类型的异常:已检查的异常、未检查的异常和错误。

检查异常是必须在方法的 throws 子句中声明的异常。它们扩展了异常,旨在成为“在您面前”的异常类型。JAVA 期望您能够处理它们,因为它们以某种方式依赖于程序之外的外部因素。已检查异常表示在正常系统操作期间可能发生的预期问题。当您尝试通过网络或文件系统使用外部系统时,通常会发生这些异常。在大多数情况下,对已检查异常的正确响应应该是稍后重试,或者提示用户修改他们的输入。

未经检查的异常(unchecked Exceptions)是不需要在 throws 子句中声明的异常。由于程序错误,JVM 不会强迫您处理它们,因为它们大多数是在运行时生成的。它们扩展了 RuntimeException。最常见的例子是 NullPointerException,未经检查的异常可能不应该被重试,正确的操作通常应该是什么都不做,并将其从您的方法和执行堆栈中删除。

错误是绝对不可恢复的严重运行时环境问题。例如 OutOfMemoryError、LinkageError 和 StackOverflowError,通常会使程序崩溃。

所有不是 Runtime Exceptions 的异常统称为 Checked Exceptions,也称为受检异常。此类异常的发生并不是程序本身的问题,而通常是由外部因素引起的。为了防止这些异常发生时程序中断或错误结果,Java要求在编写可能产生此类异常的程序代码时必须进行异常处理。

Java 语言将所有从 RuntimeException 类或 Error 类派生的异常称为未经检查的异常。

Java异常层次结构图如下图所示:

现在我们了解了异常的基本概念和分类,让我们开始探索异常处理的最佳实践。

异常处理最佳实践

不要忽略捕获的异常

虽然捕获了异常但什么也没做,除非你确定异常可以被忽略,否则你不应该这样做。这将使外界无法得知该方法发生了错误,也无法确定定位错误的原因。

在你的方法中抛出一个特定的检查异常

一定要避免上面的代码示例,它违背了检查异常的目的。声明您的方法可能抛出的特定检查异常,如果它们太多,您应该将它们包装在您自己的异常中并将信息添加到异常消息中。如果可能,您还可以考虑代码重构。

捕获具体的子类而不是异常类

捕获异常的问题在于,如果稍后调用的方法将新的已检查异常添加到其方法声明中,则开发人员的意图是应该处理具体的新异常。如果您的代码只是捕获异常(或 Throwable),您将永远不会知道更改,并且您的代码现在有错误并且可能在运行时的任何时候中断。

永远不要捕获 Throwable 类

这是一个比较严重的麻烦,因为 Java Error 也是 Throwable 的子类,Error 是 JVM 本身无法处理的不可逆情况,而对于一些 JVM 实现,JVM 可能实际上甚至不会调用 Error 上的 catch 子句。

始终将异常正确包装在自定义异常中,以免堆栈跟踪丢失

这会破坏原始异常的堆栈跟踪并且总是错误的,正确的做法是:

记录异常或抛出异常,但不要两者都做

如上面的代码,记录和抛出异常会在日志文件中产生多条日志消息,代码中存在单一问题,对试图分析日志的同事不友好。

永远不要在 finally 块中抛出任何异常

上面的代码只要 cleanUp() 从不抛出任何异常就可以了,但是如果 someMethod() 抛出异常,并且在 finally 块中,cleanUp() 抛出另一个异常,程序只会在第一个异常出现两个异常时抛出抛出,原始的第一个异常(具有正确的原因)将永远丢失。如果 finally 块中调用的代码可能引发异常,请务必处理它或记录它。永远不要让它被抛出 finally 块。

始终只捕获实际可处理的异常

这是最重要的概念,不要为了捕获异常而捕获异常,只有在要处理异常时才捕获异常,或者要在该异常中提供额外的上下文信息。如果你不能在 catch 块中处理它能让线程停止执行的有,最好的建议是不要仅仅为了重新抛出它而抓住它。

不要使用 printStackTrace() 语句或类似语句

完成代码后不要忽略 printStackTrace(),其他人可能最终会使用这些堆栈并且完全不知道如何处理它,因为它不附加任何上下文信息。

对于不打算处理的异常,使用 finally

如果在您的方法中您正在访问方法 2 并且方法 2 抛出了一些您不想在方法 1 中处理的异常,但在发生异常时仍想进行一些清理,那么这是一个很好的做法,然后最后在块中进行清理,不要使用 catch 块。

图片[1]-关于Java异常处理的最佳实践,你了解多少?-老王博客

记住早扔晚接的原则

这可能是关于异常处理最著名的原则。简单地说,应该尽快抛出异常,并尽可能晚地捕获。应该等到有足够的信息来正确处理它。

该原则暗示您将更有可能将其置于低级方法中,您将在其中检查单个值是否为空或不合适。而且您将让异常堆栈跟踪上升几个级别,直到您达到足够的抽象级别来处理问题。

异常处理后清理资源

如果您正在使用数据库连接或网络连接等资源,请务必清除它们。如果您调用的 API 仅使用未经检查的异常,您仍应使用 try-finally 块来清理资源。在try模块中访问资源,在finally中关闭资源。即使在访问资源时发生任何异常,资源也会正常关闭。

只抛出与方法相关的异常

相关性对于保持应用程序清洁非常重要。尝试读取文件的方法能让线程停止执行的有,如果抛出 NullPointerException,则不会向用户提供任何相关信息。相反,如果将此类异常包装在自定义异常中会更好。NoSuchFileFoundException 对使用此方法的用户更有用。

永远不要在程序中使用异常来进行流控制

不要使用异常来处理项目中的应用程序逻辑。永远不要这样做,它会使代码难以阅读和理解。

尽早验证用户输入以在请求处理的早期捕获异常

始终在非常早的阶段验证用户输入,甚至在到达控制器之前,这将帮助您最大限度地减少核心应用程序逻辑中异常处理代码的数量。它还保证了在用户输入错误的情况下与应用程序的一致性。

例如:如果在用户注册应用程序中,遵循以下逻辑:

验证用户

插入用户

验证地址

插入地址

如果出现问题,回滚所有内容

这不是正确的做法,它会使数据库在各种情况下处于不一致的状态,首先应验证一切,然后将用户数据放入 dao 层并更新数据库。正确的方法是:

验证用户

验证地址

插入用户

插入地址

如果出现问题,则回滚所有内容

一个异常只能包含在一个日志中

不要像上面那样做,对多个 LOGGER.debug() 调用使用多行日志消息在您的测试用例中可能看起来不错,但是当它在具有 100 个并行运行的线程的应用程序服务器的日志文件中显示时,所有消息都输出到同一个日志文件中,即使它们在实际代码中是前后行,但两条日志消息在日志文件中可能相隔超过 100 行。应该做:

尽可能将所有相关信息传递给异常

有用的异常消息和堆栈跟踪非常重要,如果你的日志找不到异常,那么日志记录有什么用?

终止被中断的线程

InterruptedException 异常提示停止程序正在做的事情,例如事务超时或线程池被关闭。

而不是忽略 InterruptedException,您应该尽最大努力完成您正在做的事情,并完成当前正在执行的线程。修改后的程序如下:

对于重复的try-catch,使用模板方法

代码中有很多类似的catch块是没有用的,只会增加代码的重复,对于这样的问题可以使用模板方法。

例如,尝试关闭数据库连接时的异常处理。

这种方法会在应用程序的很多地方使用。不要把这段代码到处乱写,而是定义上面的方法并像这样使用它:

使用 JavaDoc 记录应用程序中的所有异常

养成使用 JavaDoc 记录运行时可能引发的所有异常的习惯,并尝试包含用户在发生这些异常时应遵循的操作。

总结

本文首先介绍了异常是什么以及它的三类分类,然后通过20个最佳实践来讨论如何处理异常,希望在以后的异常处理方面有所改进和见解。

© 版权声明
THE END
喜欢就支持一下吧
点赞0
分享
评论 抢沙发

请登录后发表评论