作者 | 武培轩
责编 | 屠敏
出品 | CSDN 博客
反常处理是 Java 开发中的一个重要部分,是为了处理任何过错状况,比方资源不行拜访,不合法输入,空输入等等。Java 供给了几个反常处理特性,以try,catch 和 finally 关键字的办法内建于言语自身之中。Java 编程言语也答应创立新的自界说反常,并经过运用 throw 和 throws关键字抛出它们。在Java编程中,Java 的反常处理不单单是知道语法这么简略,它有必要遵从规范的 JDK 库,和处理过错和反常的开源代码。
这儿咱们将评论一些关于反常处理的 Java 最佳实践。在咱们评论反常处理的最佳实践之前,先让咱们了解下几个重要的概念,那便是什么是反常以及反常的分类。
什么是反常?
反常的英文单词是 exception,反常本质上是程序上的过错,包含程序逻辑过错和体系过错。比方运用空的引证、数组下标越界、内存溢出过错等,这些都是意外的状况,违背咱们程序自身的目的。过错在咱们编写程序的进程中会常常发作,包含编译期间和运转期间的过错,在编译期间呈现的过错有编译器协助咱们一同批改,可是运转期间的过错便不是编译器量力而行了,并且运转期间的过错往往是难以预料的。假若程序在运转期间呈现了过错,假如置之脑后,程序便会中止或直接导致体系溃散,明显这不是咱们期望看到的成果。
怎么对运转期间呈现的过错进行处理和弥补呢?Java 供给了反常机制来进行处理,经过反常机制来处理程序运转期间呈现的过错。经过反常机制,咱们能够更好地提高程序的健壮性。
反常分类
Java 把反常当作目标来处理,并界说一个基类 java.lang.Throwable 作为全部反常的超类。
Java 包含三种类型的反常: 查看性反常(checked exceptions)、非查看性反常(unchecked Exceptions) 和过错(errors)。
查看性反常(checked exceptions) 是有必要在办法的 throws 子句中声明的反常。它们扩展了反常,旨在成为一种“在你面前”的反常类型。JAVA期望你能够处理它们,由于它们以某种办法依赖于程序之外的外部要素。查看的反常表明在正常体系操作期间或许发作的预期问题。当你测验经过网络或文件体系运用外部体系时,一般会发作这些反常。大多数状况下,对查看性反常的正确呼应应该是稍后重试,或许提示用户修正其输入。
非查看性反常(unchecked Exceptions) 是不需要在throws子句中声明的反常。由于程序过错,JVM并不会强制你处理它们,由于它们大多数是在运转时生成的。它们扩展了 RuntimeException。最常见的比方是 NullPointerException, 未经查看的反常或许不应该重试,正确的操作一般应该是什么都不做,并让它从你的办法和履行仓库中出来。
过错(errors) 是严峻的运转时环境问题,必定无法康复。例如 OutOfMemoryError,linkageError 和 StackOverflowError,一般会让程序溃散。
全部不是 Runtime Exception 的反常,统称为 Checked Exception,又被称为查看性反常。这类反常的发作不是程序自身的问题,一般由外界要素形成的。为了防备这些反常发作时,形成程序的中止或得到不正确的成果,Java 要求编写或许发作这类反常的程序代码时,一定要去做反常的处理。
Java 言语将派生于 RuntimeException 类或 Error 类的全部反常称为非查看性反常。
Java 反常层次结构图如下图所示:
在了解了反常的基本概念以及分类后,现在让咱们开端探究反常处理的最佳实践吧。
反常处理最佳实践
不要疏忽捕捉的反常
尽管捕捉了反常可是却没有做任何处理,除非你坚信这个反常能够疏忽,否则不应该这样做。这样会导致外面无法知晓该办法发作了过错,无法确认定位过错原因。
在你的办法里抛出界说详细的查看性反常
一定要防止呈现上面的代码示例,它破坏了查看性反常的目的。声明你的办法或许抛出的详细查看性反常,假如只需太多这样的查看性反常,你应该把它们包装在你自己的反常中,并在反常音讯中添加信息。假如或许的话,你也能够考虑代码重构。
捕获详细的子类而不是捕获 Exception 类
捕获反常的问题是,假如稍后调用的办法为其办法声明添加了新的查看性反常,则开发人员的目的是应该处理详细的新反常。假如你的代码仅仅捕获反常(或 Throwable),永久不会知道这个改变,以及你的代码现在是过错的,并且或许会在运转时的任何时分中止。
永久不要捕获 Throwable 类
这是一个更严峻的费事,由于 Java Error 也是 Throwable 的子类,Error 是 JVM 自身无法处理的不行逆转的条件,关于某些 JVM 的完结,JVM 或许实践上乃至不会在 Error 上调用 catch 子句。
一直正确包装自界说反常中的反常,以便仓库盯梢不会丢掉
这破坏了原始反常的仓库盯梢,并且一直是过错的,正确的做法是:
要么记载反常要么抛出反常,但不要一同履行
正如上面的代码中,记载和抛出反常会在日志文件中发作多条日志音讯,代码中存在单个问题,并且对测验剖析日志的搭档很不友爱。
finally 块中永久不要抛出任何反常
只需 cleanUp() 永久不会抛出任何反常,上面的代码没有问题,可是假如 someMethod() 抛出一个反常,并且在 finally 块中,cleanUp() 也抛出另一个反常,那么程序只会把第二个反常抛出来,本来的第一个反常(正确的原因)将永久丢掉。假如在 finally 块中调用的代码或许会引发反常,请确保要么处理它,要么将其记载下来。永久不要让它从 finally 块中抛出来。
一直只捕获实践可处理的反常
这是最重要的概念,不要为了捕捉反常而捕捉,只需在想要处理反常时才捕捉反常,或许期望在该反常中供给其他上下文信息。假如你不能在 catch 块中处理它,那么最好的主张便是不要只为了从头抛出它而捕获它。
不要运用 printStackTrace() 句子或相似的办法
完结代码后,切勿疏忽 printStackTrace(),终究他人或许会得到这些仓库,并且关于怎么处理它彻底没有任何办法,由于它不会附加任何上下文信息。
关于不计划处理的反常,直接运用 finally
这是一个很好的做法,假如在你的办法中你正在拜访 Method 2,而 Method 2 抛出一些你不想在 Method 1 中处理的反常,可是依然期望在发作反常时进行一些整理,然后在 finally 块中进行整理,不要运用 catch 块。
记住早 throw 晚 catch 准则
这或许是关于反常处理最著名的准则,简略说,应该赶快抛出(throw)反常,并尽或许晚地捕获(catch)它。应该比及有满足的信息来妥善处理它。
这个准则隐含地说,你将更有或许把它放在初级办法中,在那里你将查看单个值是否为空或不适合。并且你会让反常仓库盯梢上升好几个等级,直到到达满足的笼统等级才干处理问题。
在反常处理后整理资源
假如你正在运用数据库衔接或网络衔接等资源,请确保铲除它们。假如你正在调用的 API 仅运用非查看性反常,则仍应运用 try-finally 块来整理资源。在 try 模块里边拜访资源,在 finally 里边最终封闭资源。即便在拜访资源时发作任何反常,资源也会高雅地封闭。
只抛出和办法相关的反常
相关性关于坚持运用程序清洁十分重要。一种测验读取文件的办法,假如抛出 NullPointerException,那么它不会给用户任何相关的信息。相反,假如这种反常被包裹在自界说反常中,则会更好。NoSuchFileFoundException 则对该办法的用户更有用。
切勿在程序中运用反常来进行流程操控
不要在项目中呈现运用反常来处理运用程序逻辑。永久不要这样做,它会使代码很难阅览和了解。
尽早验证用户输入以在恳求处理的前期捕获反常
一直要在十分早的阶段验证用户输入,乃至在到达 controller 之前,它将协助你把中心运用程序逻辑中的反常处理代码量降到最低。假如用户输入呈现过错,还能够确保与运用程序共同。
例如:假如在用户注册运用程序中,遵从以下逻辑:
验证用户
刺进用户
验证地址
刺进地址
假如出问题回滚全部
这是不正确的做法,它会使数据库在各种状况下处于不共同的状况,应该首要验证全部内容,然后将用户数据置于 dao 层并进行数据库更新。正确的做法是:
验证用户
验证地址
刺进用户
刺进地址
假如问题回滚全部
一个反常只能包含在一个日志中
不要像上面这样做,对多个 LOGGER.debug() 调用运用多行日志音讯或许在你的测试用例中看起来不错,可是当它在具有 100 个并行运转的线程的运用程序服务器的日志文件中显现时,全部信息都输出到相同的日志文件,即便它们在实践代码中为前后走,可是在日志文件中这两个日志音讯或许会距离 100 多行。应该这样做:
将全部相关信息尽或许地传递给反常
有用的反常音讯和仓库盯梢十分重要,假如你的日志不能定位反常方位,那要日志有什么用呢?
中止掉被中止线程
InterruptedException 反常提示应该中止程序正在做的工作,比方业务超时或线程池被封闭等。
应该尽最大努力完结正在做的工作,并完结当时履行的线程,而不是疏忽 InterruptedException。修正后的程序如下:
关于重复的 try-catch,运用模板办法
在代码中有许多相似的 catch 块是无用的,只会添加代码的重复性,针对这样的问题能够运用模板办法。
例如,在测验封闭数据库衔接时的反常处理。
这类的办法将在运用程序许多当地运用。不要把这块代堆放的处处都是,而是界说上面的办法,然后像下面这样运用它:
运用 JavaDoc 中记载运用程序中的全部反常
把用 JavaDoc 记载运转时或许抛出的全部反常作为一种习气,其间也尽量包含用户应该遵从的操作,以防这些反常发作。
总结
这篇文章首要介绍了什么是反常,以及反常的三种分类,然后经过 20 个最佳实践来评论怎么处理反常,期望能在今后反常处理的时分有所改进及感悟。
版权声明:本文为CSDN博主「武培轩」的原创文章。
想为博主点赞?
想要讨教博主?