这是我写的。这里的大部分内容 ,我仍然赞同。只有最后的异常类层次:我现在倾向于使用RuntimeException作为顶层类。
另外,异常还是要少用。异常太多也吃性能。能通过校验获取结果的话,就用校验。
WHY
异常是每个人都不愿意遇到,但是又都绕不开的东西。它标识着某种错误的、或者不正常的情况,需要业务人员换一种操作方式或者数据、需要运维人员修正系统环境、或者需要开发人员修改代码。
测试人员小A接到了一个待测版本。部署好服务器和数据库、点击"保存并提交文档”按钮之后,系统报错了:“请联系管理员处理!”
但是小A不知道错在什么地方。于是她去找开发人员小乙。
小乙放下手头的工作,花了一个小时,发现是数据库中少了一条错误信息。于是他把原因告诉了小A,并且把报错信息改为:“错误信息:系统参数未正常初始化!”
这样,后来的测试同事们遇到这个问题,都不用再找开发,而直接通过操作数据库解决了。于是测试happy了,开发也happy了,他们过上了幸福快乐的生活……
异常规范的作用,就在于明确指出异常原因、包含信息,以及处理方式。这样,可以避免为了一个简单的异常投入过多的测试、开发或运维人力。
HOW
禁止“吃掉”异常
绝对禁止在catch到异常后,留下一个空的catch块(或者仅仅使用e.printStackTrace()来打印异常信息)。
如果无法“恢复现场”,则至少应当在catch块中,记录下异常的上下文日志(使用LOGGER.error(...)级别)。
如果可以“恢复现场”,则应当尽量保持流程可继续执行。
如下示例。parseTime方法首先按照TIME_FORMAT格式("yyyy-MM-dd HH:mm:ss")格式来解析时间;如果解析过程中发生异常,再使用DATE_FORMAT格式("yyyy-MM-dd")格式来解析。如果仍然解析失败,则记录日志,并返回nul值。
private static Date parseTime(String date) {
Date time = null;
try {
time = DateUtils.parseDate(date,OasisLendRequestBuilder.TIME_FORMAT);
} catch (ParseException e) {
try {
time = DateUtils.parseDate(date,OasisLendRequestBuilder.DATE_FORMAT);
} catch (ParseException e1) {
OasisLendRequestBuilder.logger.error("无法解析日期:{}", date, e);
time = null;
}
}
return time;
}
异常日志应记录异常堆栈信息
堆栈信息的重要性是不言而喻的。
但是,如果用e.printStackTrace()来打印异常堆栈,这些信息不会被记录到日志中(如thread.log或thread_error.log)。
如果使用LOGGER.error(e.getMessage()),又无法记录到异常堆栈。
为了在日志中记录下异常堆栈信息,我们应当保证在LOGGER.error(...)方法的参数列表中,异常实例是最后一个参数。这样,LOGGER会自动把异常堆栈信息输出到日志中。
将异常堆栈信息打印到日志中
例如,有如下代码:
try {
lendRequest = this.builder
.buildAppLendRequestFromOasis(oaLendRequest);
} catch (Exception e) {
OasisAuditResultProcessor.logger.error("opId={}",
operation.getOperation_id(), e);
throw e;
}
上述代码得到的日志
2015-10-13 16:51:34,115 [ERROR] OasisAuditResultProcessor#processFinalAppLendRequest()@218 - opId=ff808081505f1f6701505f4ef83805b3
java.lang.NullPointerException
at cn.youcredit.thread.task.oasis.OasisLendRequestBuilder.buildAppLendRequestFromOasis(OasisLendRequestBuilder.java:1032)
at cn.youcredit.thread.task.oasis.OasisAuditResultProcessor.processFinalAppLendRequest(OasisAuditResultProcessor.java:216)
at cn.youcredit.thread.task.oasis.OasisAuditResultProcessor$$FastClassBySpringCGLIB$$9e319624.invoke(<generated>
)
at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204)
父类throws声明中,尽量声明异常超类
相信THREAD组各位都见过这个问题。
类A的子类A1在重写方法service()时,声明了throws 某个异常。但是类A的方法service()内,没有任何地方抛出了这个异常。
结果,无论把InvalidDataException声明在什么地方,IDE(如eclipse)都会报错。
这种情况下,应当在父类中声明throws 异常的超类;子类中声明具体的异常类。
如下示例。handlePayProcess方法继承自父类中的方法。但子类方法多抛出了一个异常“JiBXException”。这个异常无论放在父类声明或子类声明中,eclipse都会提示出错。这时,将父类的throws声明改为 throws Exception,即可解决问题。
publc class Father{
public void handlePayProcess()throws IOException{
// 略
}
}
public class Son extends Father{
/** 这里多出来的JiBException会导致编译报错。 */
@Override
public void handlePayProcess()throws IOException, JiBException{
// 略
}
}