Java中的异常处理可以提高系统的健壮性以及用户体验。Throwable是所有异常和错误的父类,其继承结构如下:
a) Unchecked Exception
非检查异常不要求程序员捕获,且可以由系统自动抛出, 如除法运算中除数为0时,程序自动抛出ArithmeticException的算术异常,而不用程序员手动抛出。RuntimeException表示代码本身存在BUG。
b) Checked Exception
检查异常强制要求程序员捕获异常,否则编译不通过,假如将 SQLException 定义为非检测异常,开发人员可能不会捕获处理异常。这样操作数据发生异常时,会导致严重的 Connection 不关闭、Transaction 不回滚、DB 中出现脏数据等情况。将SQLException 定义为检测异常,才会驱使开发人员去显式捕捉,并且在代码产生异常后清理资源。当然清理资源后,可以继续抛出非检测异常,阻止程序的执行。检测异常大多可以应用于工具类中。
Java提供的检查异常机制,使得调用时可以明确知道该函数必须用try-catch捕获,而不用查看源代码才知该函数中存在异常。
常见问题
1. 代码层次结构的污染
上面的代码中直接抛出了检查异常,从代码耦合角度上看,调用层需显示捕获。根据设计隔离原则,可以使用非检测异常进行封装,使得调用层可以不受该函数影响。
public Order getOrderById(Long id) throw SQLException { //根据 ID 查询数据库 } /* 改正后 */ public Order getOrderById(Long id) { try{ //根据 ID 查询数据库 }catch(SQLException e){ throw new RuntimeException(SQLErrorCode, e); //利用非检测异常封装检测异常,降低层次耦合 }finally{ //关闭连接,清理资源 } }
2. 将异常包含在循环语句块中
异常处理会占用系统资源,应该将try-catch语句块提取到循环外面。比如A类中有个方法a包含try-catch语句块, 而B类中循环调用a方法,这就发生了如下的问题。
for(int i=0; i<100; i++){ try{ }catch(Exception e){ //... } }
3. 异常打印问题
· 多层次打印问题,比如Dao层中打印了log, Service层中又打印一遍,这样不仅消耗系统性能,还使得日志更为混乱了。
· 异常打印信息中可以添加一些参数信息如orderId,便于追踪。
4. 利用 Exception 捕捉所有潜在的异常
如果try中可能抛出各种异常,只用一个Exception去捕捉异常,可能会丢失原始异常的有效信息。记录异常信息时,还可以使用errorCode记录状态,编译开发人员后期跟踪系统运行状态。
参考原文: http://www.codeceo.com/article/java-exception-misdirection.html