改进异常处理的 6 条建议

来源:ImportNew - 唐尤华 ,

合理地使用异常处理可以帮你节省数小时(甚至数天)调试时间。一个乘法异常会毁掉你的晚餐乃至周末计划。如果处置不及时,甚至对你的名誉都会造成影响。一个清晰的异常处理策略可以助你节省诊断、重现和问题纠正时间。下面是6条异常处理建议。

1. 使用一个系统全局异常类

不必为每种异常类型建立单独的类,一个就够了。确保这个异常类继承RuntimeException,这样可以减少类个数并且移除不必要的异常声明。

我知道你正在想什么:如果类型只有一个,那么怎么能知道异常具体是什么?我将如何追踪具体的属性?请继续阅读。

2. 使用枚举错误码

我们大多被教授的方法是将异常转为错误信息。这次查看日志文件时很好,(呃)但是这样也有缺点:

错误信息不会被翻译(除非你是Google)

错误信息不会转换为用户友好的文字

错误信息不能用编程的方式检测

将异常消息留给开发者定义也会出现同样的错误有多种不同的描述。

一个更好的办法是使用枚举表示异常类型。为每个错误分类创建一个枚举(付款、认证等),让枚举实现ErrorCode接口并作为异常的一个属性。

当抛出异常时,只要传入合适的枚举就可以了。

throw new SystemException(PaymentCode.CREDIT_CARD_EXPIRED);

现在如果需要测试异常只要比较异常代码和枚举就可以了。

catch (SystemException e) {
if (e.getErrorCode() == PaymentCode.CREDIT_CARD_EXPIRED) {
...
}
}
通过将错误码作为查找资源的key就可以方便地提供友好的国际化文本。
public class SystemExceptionExample3 {
public static void main(String[] args) {
}
public static String getUserText(ErrorCode errorCode) {
if (errorCode == null) {
return null;
}
String key = errorCode.getClass().getSimpleName() + "__" + errorCode;
return bundle.getString(key);
}
}

3. 为枚举添加错误值

在很多时候可以为异常添加错误值,比如HTTP返回值。这种情况下,可以在ErrorCode接口添加一个getNumber方法并在每个枚举中实现这个方法。

public enum PaymentCode implements ErrorCode {
SERVICE_TIMEOUT(101),
CREDIT_CARD_EXPIRED(102),
AMOUNT_TOO_HIGH(103),
INSUFFICIENT_FUNDS(104);
private final int number;
private PaymentCode(int number) {
this.number = number;
}
@Override
public int getNumber() {
return number;
}
}

添加错误码可以是全局数值也可以每个枚举自己负责。你可以直接使用枚举里的ordinal()方法或者从文件或数据库加载。

4. 为异常添加动态属性

好的异常处理还应该记录相关数据而不仅仅是堆栈信息,这样可以在诊断错误和重现错误时节省大量时间。用户不会在你的应用停止工作时告诉你他们到底做了什么。

最简单的办法是给异常添加一个java.util.Map字段。新字段的职责就是通过名字保存相关数据。通过添加setter方法可以遵循流式接口。

可以像下面示例这样添加相关数据并抛出异常:

throw new SystemException(ValidationCode.VALUE_TOO_SHORT)
.set("field", field)
.set("value", value)
.set("min-length", MIN_LENGTH);

5. 避免不必要的嵌套

冗长的堆栈信息不会有任何帮助,更糟糕的是会浪费你的时间和资源。重新抛出异常时调用静态函数而不是异常构造函数。封装的静态函数决定什么时候嵌套异常什么时候只要返回原来的实例。

public static SystemException wrap(Throwable exception, ErrorCode errorCode) {
if (exception instanceof SystemException) {
SystemException se = (SystemException)exception;
if (errorCode != null && errorCode != se.getErrorCode()) {
return new SystemException(exception.getMessage(), exception, errorCode);
}
return se;
} else {
return new SystemException(exception.getMessage(), exception, errorCode);
}
}
public static SystemException wrap(Throwable exception) {
return wrap(exception, null);
}
Your new code for rethrowing exceptions will look like the following.
catch (IOException e) {
throw SystemException.wrap(e).set("fileName", fileName);
}
6. 使用带Web支持的集中式logger

再额外附赠一个建议。可能你情况很难向产品记录日志,这个麻烦可能来自多个中间商(很多开发者不能直接访问产品环境)。

在多服务器环境下情况可能会更糟。找到正确的服务器或者确定问题影响到了哪个服务器是一件非常令人头痛的事情。

我的建议是:

将你的日志记录到一个地方,推荐记录到数据库中。

通过Web浏览器访问数据库。

有很多方法和备选产品可以达成这一目标,log collector、远程logger、JMX agent、系统监视软件等。甚至可以自己写一个。重要的是要快速行动,一旦你达成了目标,你就可以:

几秒钟之内定位错误

为每个异常增加一个URL,可以记录或者发送email

让你的伙伴可以在没有你的情况下定位错误原因

避免测试人员为同一个bug添加多个记录。他们可以在bug记录里增加一条异常URL

省钱

让你的周末和名誉不受影响

你有什么好的建议吗?

希望这些建议对你有所帮助。给异常添加正确的信息和将异常放在易于访问的地方可以避免很多灾难事故和时间浪费。如果你有一些自己的异常处理秘诀,欢迎分享。

下载

这里包含了本文的所有代码(包括Eclipse项目)。代码的发布遵循Apache 2.0协议。

http://northconcepts.com/blog-downloads/exceptions/NorthConcepts-Exceptions.zip

祝编程快乐!

本文来自企鹅号 - ImportNew媒体

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏海纳周报

用Atomic实现锁

一直想写ReentrantLock,就得先介绍AbstractQueueSynchronizer,可是我觉得这样写,不过瘾,我把代码贴一遍,懂的人自己就能找到这...

3636
来自专栏学习力

《Java从入门到放弃》框架入门篇:Struts2的常用验证方式

2098
来自专栏醒者呆

精雕细琢——全方位解析单例模式

单例模式有的时候特别重要,因为某些系统是要求某个类在整个生命周期中有且只有一个实例存在,这时候就要用到单例模式。 保证一个类仅有一个实例,并提供一个访问它的全...

3444
来自专栏为数不多的Android技巧

我为Dexposed续一秒——论ART上运行时 Method AOP实现

两年前阿里开源了Dexposed 项目,它能够在Dalvik上无侵入地实现运行时方法拦截,正如其介绍「enable ‘god’ mode for single ...

2732
来自专栏资深Tester

测试流程之如何设计测试用例

2013
来自专栏青玉伏案

设计模式(九): 从醋溜土豆丝和清炒苦瓜中来学习"模板方法模式"(Template Method Pattern)

今天是五.四青年节,祝大家节日快乐。看着今天这标题就有食欲,夏天到了,醋溜土豆丝和清炒苦瓜适合夏天吃,好吃不上火。这两道菜大部分人都应该吃过,特别是醋溜土豆丝,...

1769
来自专栏机器之心

资源 | 简单快捷的数据处理,数据科学需要注意的命令行

1655
来自专栏编程

设计模式启示录(二)

设计模式启示录(二) 在【设计模式启示录 (一)】中,重点介绍了设计模式的精髓(抽象),设计模式的分类(按抽象的目的进行分类)。在本篇中,将按照前述的七大分类,...

1807
来自专栏Java架构

Java 异常进阶Java异常简介天使变恶魔无处不在的throws手足无措的API使用者疲于应付的API用户正确地使用Checked Exception

1556
来自专栏java一日一条

有效处理Java异常的三个原则,你知道吗?

在有效使用异常的情况下,异常类型回答了“什么”被抛出,异常堆栈跟踪回答了“在哪“抛出,异常信息回答了“为什么“会抛出,如果你的异常没有回答以上全部问题,那么可能...

1701

扫码关注云+社区

领取腾讯云代金券