异常处理之最佳实践(Best Practices for Exception Handling )

本文探讨了异常处理的重要性,包括程序错误、代码调用错误及资源故障错误等不同类型的异常,并提出了最佳实践方案,如合理使用Checked和Unchecked异常、异常封装、资源清理、避免异常流程控制等。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

    关于异常处理最主要的问题是什么时间、怎么使用,在本文将含括一些关于异常处理的最佳实践方案。

 

异常的本质(这里有三种不同的情况会产生并抛出异常):

  1. 程序错误:这种异常时由于程序的错误而产生的(例如,NullPointerException and IllegalArgumentException)。
  2. 代码调用错误:客户端代码试图违反API协议调用接口。如果异常能提供一些有用的信息,客户端可以采取一些替代的行为。例如:解析一个不符合格式要求的XML文档而抛出的异常。如果这个异常能包含一些有用的信息(例如xml文档的那个位置出错),那么客户端就可以利用这些信息来采取回复步骤了。
  3. 资源故障错误:例如系统内存不足,网络连接失败。客户端可以在一段时间后重试操作,或者记录资源失败,停止服务。

 

Java中的异常种类:

在java中定义了两种异常:

  1. Checked exceptions: 继承于exception类的异常是checked exceptions, 客户端代码必须处理从API中抛出的此类异常,可以用catch块来处理,也可以用throw把他抛给外面来处理。
  2. Unchecked exceptions:继承于RuntimeException类的异常是Unchecked exceptions,但是所有继承于RuntimeException的类都得到了特殊对待,将不要求客户代码必须处理他们,因此他们被叫做Unchecked exceptions。

下图展示了NullPointerException的继承关系:他继承RuntimeException,因此是一个Unchecked exceptions.

NullpointerException

 

最佳接口设计实践

  • 当你要决定使用哪种异常的时候,你可以先问问自己“当这种异常发生时,客户端代码可以采取什么行为?”

如果客户端可以根据异常采取一些替代行为来恢复系统运行,那么就用checked exception。反之,如果客户端对异常无能为力(记录日志也算没有任何用处, 这里说的有用措施是可以从异常中恢复系统运行),那么就使用unchecked exception吧。

  • 对异常进行封装。

从来不要让具体实现中的特定异常被抛到外层,例如不要把SQLException从DAO层抛到业务逻辑层,因为业务逻辑不需要知道具体的SQLException是什么,这时我们有两种选择:

  1.  
    1. 如果客户端逻辑需要这些错误信息,那么把SQLException转换成另一种checked exception,即封装。
    2. 如果客户端逻辑不需要这些,那么把SQLException转换成另一种unchecked exception,即封装。

大多数时间里,客户端不能对这些SQLException做任何事情, 所以尽量把它们转换成unchecked exceptions. 看看下面两段代码:

 

 
如果你确定在业务逻辑层能根据DAO的异常做一些恢复操作,那么你可以把它们转换成更有意义的checked exception。
  • 不要试图去创建任何对客户端来说没有有用信息的自定义异常, 参照下面两段代码:

 

  • 为异常添加文档,方便调用者使用。

异常使用的最佳实践

  • Always clean up after yourself

如果你正在使用数据库连接或者网络连接时,一定要记住释放资源,及时你调用的接口只抛出unckecked exception, 也一定要在使用之后利用try - finally块来释放资源。

 

  • 从来不要用异常来做流程控制。

 

生成/收集内存stack traces信息是一件代价高昂的事情,stack trace是用来调试的。在try-catch块时他会自动收集stacktrace信息,降低运行速度,而异常处理只有在异常情况的时候才使用。

 

  • 不要压制/忽略/吃掉异常

 

当一个checked exception从一个API被抛出时,它是在试图告诉你要采取一些必要措施来处理他,如果异常对你来说没有任何意义,那么就把他封装成unchecked exception并抛给上一层,千万不要用catch块{}来忽略/吃掉它,当作什么事都没发生过。

 

  • 不要捕捉顶层的异常

 

事实上,Unchecked exceptions 继承于 RuntimeException 类,而它又是继承Exception类,如果捕捉Exception类的话,同时也捕捉了RuntimeException,这样也同时忽略我们本来要抛给上层的Unchecked exceptions。

 

 

  • 同一个异常只记录一次(Log exceptions just once)

同一个异常的stack trace被记录多次会给程序员检查原始异常源的日志stack trace带来不必要的麻烦/困惑,而且会带来更大的IO量,所以只记录一次。

 

 


### Java 异常处理概述 Java 中的异常处理是一种用于管理程序执行期间可能出现错误情况的机制。通过使用 `try`, `catch`, `finally` 和 `throw` 关键字,开发者能够编写更加稳健的应用程序[^3]。 #### Unchecked Exception(非受检异常) 非受检异常是指那些继承自 `RuntimeException` 的类所代表的异常状况。这些异常在编译阶段不会被强制要求捕捉或者声明抛出;然而,在实际编码实践中仍然推荐针对可能发生的情况采取预防措施并合理地处理它们。常见的例子包括但不限于: - **NullPointerException**: 当尝试访问对象实例变量或调用其方法时该对象为空(null)。 - **ArrayIndexOutOfBoundsException**: 数组索引超出范围时触发此异常。 ```java public class Example { public static void main(String[] args) { try { int[] numbers = {1, 2}; System.out.println(numbers[5]); // 将引发 ArrayIndexOutOfBoundsException } catch (ArrayIndexOutOfBoundsException e) { System.err.println("Error: Index out of bounds."); } } } ``` #### Checked Exception(受检异常) 与 unchecked exception 不同的是 checked exceptions 是指所有其他类型的异常类别——即不从 RuntimeException 继承下来的异常。对于此类异常,如果函数内部有可能发生,则必须在其签名中标明 throws 或者利用 try-catch 结构来捕获它。这样做有助于提醒使用者注意潜在的风险点,并促使他们考虑如何应对这种情形的发生。 #### Best Practices for Handling Exceptions 当涉及到最佳实践方面时,有几个要点值得注意: - 总是以最具体的方式匹配异常类型而不是简单地抓取所有的 Throwable 对象; - 使用 finally 块确保资源释放即使发生了未预期事件也能得到妥善清理; - 记录日志以便于后续调试分析; - 向用户展示友好提示而非技术细节信息防止暴露敏感数据给外部人员查看。 #### Common Interview Questions on Java Exception Handling 一些关于 Java 异常处理的经典面试题目如下所示: - 描述一下什么是 checked 和 unchecked 异常? - 如何区分这两者的区别以及各自适用场景是什么? - 如果在一个多线程环境中遇到了异常应该怎样做才能不影响整个系统的稳定性?
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值