在两种情况下应该捕获异常。
1. 在最低级别处理
这是你与第三方代码集成的级别,例如ORM工具或执行IO操作(访问HTTP资源、读取文件、保存到数据库等)的任何库。也就是说,离开应用程序原生代码与其他组件交互的级别。
在这个级别上,不受你控制的意外问题可能会发生,例如连接失败和文件被锁定。
你可能想通过捕获TimeoutException
来处理数据库连接故障,以便几秒钟后重试。同样,当访问文件时出现异常,文件可能正在被进程锁定,但在下一时刻可用。
在这种情况下,以下是指南:
- 只处理特定的异常,例如
SqlTimeoutException
或IOException
。永远不要处理通用异常(类型为Exception
的异常)
- 只有当有实际意义的处理方式时才处理异常,例如重试、触发补偿操作或向异常添加更多数据(例如上下文变量),然后重新抛出异常
- 不要在此处执行日志记录
让其他异常向上传播,因为它们将由第二个情况处理。
2. 在最高级别上
这将是在异常直接抛出到用户之前可以处理异常的最后一个位置。
您的目标是记录错误并将详细信息发送给程序员,以便他们可以识别和纠正错误。添加尽可能多的信息,记录下来,然后向用户显示道歉消息,特别是如果这是软件中的错误,他们可能无法做任何事情。
在这种第二种情况下的指导方针是:
- 处理泛型Exception类
- 从当前执行上下文中添加更多信息
- 记录错误并通知程序员
- 向用户表示歉意
- 尽快解决问题
这些指导方针背后的原因
首先,异常代表不可逆转的错误。它们代表系统中的错误、程序员犯的错误或应用程序无法控制的情况。
在这些情况下,通常用户无法做什么。因此,你唯一能做的就是记录错误,采取必要的补救措施,并向用户道歉。如果是程序员犯的错误,最好让他们知道并修复它,努力实现更稳定的版本。
其次,try catch块根据使用方式可能会掩盖应用程序执行流程。try catch块的功能类似于标签及其goto伴侣,会导致应用程序执行流从一个点跳转到另一个点。
何时抛出异常
在开发库的背景下更容易解释。当你遇到错误并且除了让API的使用者知道这个错误并让他们决定怎么做之外,你应该抛出异常。
想象一下,你是某个数据访问库的开发人员。当你遇到网络错误时,除了抛出异常外,你无法做任何事情。从数据访问库的角度来看,这是一种不可逆转的错误。
但是,在开发网站时情况就不同了。你可能会捕获这样的异常以进行重试,但如果从外部层面接收到无效参数,则会抛出异常,因为这些参数应该在那里进行验证。
在表示层中,情况又有所不同,您希望用户提供无效参数。在这种情况下,只需显示友好的消息而不是抛出异常。
如https://roaddd.com/the-only-two-cases-when-you-should-handle-exceptions/中所述。