首页 Java 异常实践总结
文章
取消

Java 异常实践总结

本文对异常处理的种类进归纳, 试图总结出一种好的异常处理的实践方法

为了更好的描述异常处理方法, 先把项目分成两大类, 当工作在不同的项目时, 程序员需要站在不同的位置上来采用合适的异常处理方法.

一, 应用程序中的异常处理

主要是指程序员是在开发一个被用户使用的程序, 例如, 一个桌面软件, 一个web应用服务器, 或者一个网络应用服务器.

这类程序的工作就是要帮助用户完成一个任务, 并把结果返回给用户.

而这类程序的开发人员处于一个中间地位: 调用各种支持接口, 处理业务逻辑, 最终把处理结果返回给最终用户, 在这个过程中, 会和3种异常打交道:

  1. 调用其他支持接口时, 其他接口可能会抛出 checked 异常, 例如调用 DAO接口遇到一个数据库异常
  2. 调用其他支持接口时, 其他接口抛出了 RuntimeException
  3. 处理业务逻辑过程中, 发现正常业务不可能完成, 需要给用户返回一个特殊的响应消息. 例如, 用户提交的买火车票请求中的车次是不存在的

当遇到这3种情况, 只有两种处理方法:

  1. 捕获到 checked exception 后, 自己能处理掉, 就自己处理掉了, 不需要通知最终用户 (其实我想不出这种例子, 从对用户友好的角度来说, 操作失败后, 至少要给用户一个提示吧?)
  2. 其他需要给用户返回合理的提示的情况: 简单而有效的处理方法就是抛出一个带有异常信息的 RuntimeException, 这个 RuntimeException 会在程序最外层被捕获统一处理, 并根据异常信息返回相应的响应消息给客户,

有两点特别说明一下:

  • 这里抛出的 RuntimeException, 主要是指抛出 RuntimeException 的一个子类 (可以是Java提供的, 也可以是自己定义的). 这样的好处是对异常的描述性更强, 可读性好
  • 抛出 RuntimeException 而不是抛出 checked exception 的好处是, 这种情况抛出异常的目的只是为了让程序能返回响应消息给客户, 如果抛出 checked exception, 需要一层层的抛, 一层层的捕获上去, 才能在最外层返回响应给客户, 这样就几乎回退到古老的返回值的异常处理模式, 失去利用异常类的好处

二, 框架开发和库开发中的异常处理

这里主要指的是程序员开发一个框架或者一个库给其他的程序员使用, 例如, 开发一个 HTTP 客户端请求的封装包, 或开发一个容器框架等工作.

和开发应用程序不同, 这类工作的开发人员在处理异常时需要做更多的思考, 应用程序的开发很多时候只需要给客户一个提示就行了, 而开发给他人使用的包, 根本上来说, 是要制定一个契约, 需要考虑这个契约是否合理, 是否能被别人方便的使用.

在做这类开发时, 只会在2种情况下和异常打交道:

  1. 调用其他支持接口时, 其他接口可能会抛出异常 (可能是 checked 也可能是 unchecked 的异常)
  2. 当正常流程不可能完成时, 需要提供一个异常消息给调用者, 这个时候需要给提供给使用者的接口定义一个异常消息的返回

这两种情况, 其实统一起来, 就是定义接口的时候, 需要考虑, 在正常逻辑不能完成的时候, 不能提供正常的返回给调用者, 需要以什么样的形式通知调用者出现了例外情况.

一般来说有几种通知方法:

  • 返回一个特殊的值 (最普遍的, 返回一个 null, 或者一个空对象, 例如返回数组时, 返回一个长度为0的数组)
  • 返回一个 checked exception, 调用者必须在调用的时候捕获
  • 返回一个 unchecked exception

开发者需要在设计接口时具体选择哪一种处理方式, 需要开发者站在调用者的角度思考后才能确定. 也就是说需要思考, 调用者在接收到这个例外消息以后, 需要怎么处理这个例外.

主要分2种情况:

  1. 可以确定是一个不可恢复的错误, 也就是说可以确定调用者接收到这种例外以后, 只能是把这个错误通知最终用户 (例如, 数据库异常, 文件打不开, 网络不可用等). 这个时候, 直接抛出一个 RuntimeException (一般来说是 RuntimeException 的子类),
  2. 调用者收到这种例外以后, 有可能根据具体的异常信息, 采用相应的恢复动作, 这种例外其实是属于业务逻辑的一种非正常状态. 例如, 火车票票价查询请求, 而请求中的车次是不存在的. 这种情况下, 调用者可以根据不同的异常消息采用不同的动作. 可以采用的处理方法:
  • 用返回值通知调用者. (当返回值是一个字符串时, 很多时候返回一个长度为0的字符串比抛出一个 checked exception 要好)
  • 用 checked exception 通知调用者, 好处是带更多种类的信息, 并强制提醒调用者进行处理. 坏处是一旦接口公布出去, 以后再想改动基本就不可能了, 所以抛出 checked exception 需要要深思熟虑

另外这个实践方案不止适用于java异常处理,其他语言的异常处理也可以参考

三, Reference

本文由作者按照 CC BY 4.0 进行授权