Robust Java 中文版---Java异常处理、测试与调试,这本书主要讲解了Java异常 的原理及处理方法,系统地阐述了体系结构、设计、开发、测试和调试等主题,并讨论了如何构建健壮的系统。
一、异常基础
java异常的产生是代码处,异常会作为消息发送至应用程序的catch部分或者抛出。
1.1 Throwable的内部关系
异常的祖先是Throwable(可抛出),底下是Exception(异常)和Error(错误)
Exception异常:为“合理程序程序需要捕获的情况”,可预测,可恢复。
Error错误:合理应用程序不应试图捕获它,反常情况。
1.2 可检测异常与非检测异常
区别:可检测异常经编译器验证,而非检测异常不需要。
主要的非检测异常:RuntimeException和Error。(对于RuntimeException,一般针对特定操作,请增加校验。)
1.3 Trowable的属性
Throwable 存储3个属性:
- message(消息):String //描述性文本 读(只能在构造函数设置)
- stack trace(栈跟踪):StackTraceElement[] //引发异常的所有方法调用的记录 读-写
- cause(原因):Trowable //产生此异常的原因 读-写(只能设置一次) getCause和initCause获取和设置,或者构造函数设置
除3个属性外,Throwable还包含toString和getLocalizedMessage 方法。
toString方法:覆盖Object类,返回异常名(对于非空消息字符串)。
getLocalizedMessge方法:返回消息字符串的值。
二、异常的实践
2.1 处理还是声明异常
经验法则:尽可能去处理异常,如果没有能力处理就声明异常。
从本质上讲,仅当方法缺少自我处理异常的信息、上下文或资源时,才将异常信息传给调用者。
三、异常高级特性
3.1 链表异常
就是异常允许设置两个异常之间的关联,称为链表异常。参考1.3里的cause属性。
设置方法有两种,作为构造函数参数传入,或者调用initCause(Throwable)方法设置,只能设置一次。
一般不常用,处理链表异常的方式可以参考如下:
try{
throublesomeObject.riskyMethod();
}catch(ChainedException exc){
System.out.println(“We’re in trouble now!!!”);
Throwable currentException = exc;
do{
System.out.println(currentException.toString());
currentException = currentException.getCause();
}while(currentException != null)
}
3.2 异常的本地化和国际化
如果需要本地化异常相关文本,可在自定义异常中覆盖getLocalizedMessage(),以加载本地专用的异常信息。
java使用java.util和java.text包的类支持110n和i18n.java.util.resourceBundle和java.util.Locale是两个关键类。
实现步骤为:
- 创建ResourceBundle子类来存储消息
- 为不同地区继承ResourceBundle类
- 创建覆盖getLocalizedMessage的自定义异常类并用ResourceBundle检索消息
- 具体大家自己查下吧,就不贴代码了。
3.3 不声明异常,声明异常,try-catch处理异常的性能比较
有兴趣的可以自己写个测试类,javap -c -verbose一下。
可以看到:
声明异常和不声明的两个方法字节码相同,声明异常的抛出语句在类的方法查找表中声明。
而内部try-catch的方法,会增加一些如goto, astore等的字节码指令,以及一个与该方法相关的异常表(Exception table)。若产生异常,异常表控制代码路由。实际上行为与前两者类似。
那么这三者的性能比较会怎样?实际测试是区别不明显。
总结下最佳实践:
尽量避免抛出异常。
如果条件允许就处理异常。
如果条件不允许就声明异常。
四、异常与多线程
4.1 多线程与异常的关系
看run方法签名,知道线程的异常都必须内部处理。
当run方法抛出非检测异常(RuntimeException或Error)时,负责该执行路径的Thread将终止,而其他线程无感知,理想情况下,多线程不抛出受检或非受检异常。
关于dumpstack:
多线程程序中,每个线程都有自己的执行路径,也有自己的栈跟踪。要分析Thread的栈跟踪,调用Thread类的dumpStack方法,将其显示在标准错误流中(System.err)。
如果正常运行时执行该操作,显示Thread在其调用栈的当前位置。
如果产生异常,则显示标准异常栈跟踪。
4.2 同步代码块中的异常
比较正确的写法:除非保证不损坏同步块,决不抛出传到同步块意外的异常:
try{
synchronized(this){
}
}catch(RuntimeException exc){}
五、异常设计的最佳实践
- (1)在应用程序中,可根据潜在故障集来描述各个用例。可根据在运行程序中出现的可能性及严重程度,按层次定义这些故障。
- (2)只解决对程序有重大影响且不可避免的问题和错误。
- (3)方法只传递程序中需要广而告之的错误,或传递因为方法缺少上下文或资源而无法解决的错误。
- (4)方法应根据使用者的角色和职责,以使用者可轻易理解的方式传递错误。
- (5)方法仅在以下情况下抛出多个异常:
a.互相排斥(起因不同)
b.对使用者有不同作用
c.表示完全不同的故障 - (6)在任何软件实体中(类、组件、系统、架构或API),异常都应称为实体合约(实体与外部的接口)的标准部分。
六、测试技术体系
6.1 测试角色和职责
- 6.1.1 开发人员(TDD适合开发人员的单元测试,集成测试)
白盒:静态和动态测试,包括单元,集成,烟雾(smoke)和回归测试
体系结构:大多数测试形式,但有效性测试最好由用户来执行 - 6.1.2 测试人员
黑盒:大多数测试形式,特别是探索和基于风险的测试
系统结构:大多数测试形式 - 6.1.3 用户
黑盒:alpha,beta和用户满意度测试
体系结构:有效性测试
七、调试技术体系
低级:code-resident技术
- 日志记录API
- 异常和错误
- 专用测试方法
中级:模式和架构技术
- 收集器(被动数据收集)
- 监控器(被动数据监控)
- 模仿器(主动被调用行为)
- 模拟器(主动调用行为)
高级:系统技术
- 体系结构测试方法和架构
- 基础结构收集方法
- JPDA(java平台调试器体系结构)
参考:
- 《Robust Java 中文版》:http://book.douban.com/subject/1681498/
(转载本站文章请注明作者和出处 Vernon Zheng(郑雪峰) – vernonzheng.com ,请勿用于任何商业用途)