Java异常-《Robust Java 中文版》 笔记总结

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平台调试器体系结构)

参考:

(转载本站文章请注明作者和出处 Vernon Zheng(郑雪峰) – vernonzheng.com ,请勿用于任何商业用途)