java提供了两类异常,一种是可检查异常,另一种是非可检查异常。所有RuntimeException及其子类都是非可检查异常,其余为可检查异常。java的本意是程序员需要负责处理可检查异常,而非可检查异常则不做强制要求。
对于可检查异常,在程序中是必须要处理的,要么捕获,要么继续抛出。如果当前程序可以处理并恢复,就把异常捕获下来处理掉,如果程序没有能力处理就继续抛出。
捕获异常的时候___不要捕获Throwable,Exception这样范围过大的异常___。异常中包含关键信息的是异常的类型,捕获范围太大无法对异常分情况处理。也不要图省事捕获异常后仅仅打印异常栈而不处理。
以上都是最基础的东西,基本上涉及java异常处理的教程上都能找到。但是仅仅知道这些还远远不够。其实java的异常处理真的是一件很烦人的事情。
对于一部分可检查异常,的确是可以处理并恢复的。比如网络连接失败,我们可以尝试继续连接;比如用户输入了错误的数据,我们可以提示用户重新输入。 然而很多可检查异常也不是我们有能力处理的。有些异常发生的时候我们已经打开了一些资源,如数据库连接或文件流,我们捕获异常后可以做的事情很有限,只能做一些善后工作,在finally块中将打开的资源关闭。
有一种说法是当程序捕获异常后不知道如何处理,再抛出另外一种异常。底层的异常信息发给上一层或用户往往并不能提供可理解的有效信息。比如底层发生了一个IOException,直接抛给调用者或最终用户可能并没什么用,根据异常发生时的使用场景抛出“图片上传失败”或者“配置文件读取失败”会更明确。
这个看上去很有道理但是可操作性上有问题。文件打不开,可能是用户输入的文件路径有错,也可能是文件意外丢失。用户输入有误可以提示用户重新检查自己的输入,但是文件丢失是没办法处理的。不能处理就要再次抛出,无论原样抛出还是抛出其他异常,上层的调用者仍无法处理。如果将异常一层层抛出,最后抛给虚拟机,程序会异常退出。但是对于web应用,不能因为一个异常就随随便便退出。异常会在容器或web框架中被处理掉,通常是一个请求收到了一个错误消息,比如500 服务器内部错误。
为什么明知道处理不了还要捕获并继续抛出呢?类库的编写者可能认为认为异常可以被处理于是声明抛出可检查异常,但是实际情况却有可能是程序根本没办法处理。由于程序运行环境造成的异常基本上是不可能由程序处理并恢复的。这种情况下可以将异常捕获,再抛出一个非可检查异常。抛出异常前可以记录一下日志,方便问题追踪。
由于程序员的错误产生的异常也可以捕获后抛出非可检查异常。比如图片格式转换抛出的“格式不支持”异常,这种异常最常见的情况是程序员把格式名弄错了。
总结下几类常见异常的处理方法:
可以处理的异常——捕获并处理异常,恢复程序的正常运行
本级程序无法处理的异常——抛出另一种更明确的异常
在当前应用场景下无法处理的异常——抛出一个非可检查异常
除非是程序员出错否则几乎不可能发生的异常——抛出一个非可检查异常
异常发生时有打开的资源——若不能处理则要注意在finally中关闭资源
由于捕获后要抛出非可检查异常的情况很常见,可以看看使用的web框架有没有可用的异常,如果没有也可以自己定义一个应用程序级别的异常。