1、语言提供内建一致的错误处理机制,避免不一致的错误处理方式和风格。其基本思想是,让错误源将合适的信息传到某个接收者进行处理;这个接收者可能与错误源位于同一抽象层次,更可能位于更高的抽象层次。做个简单的类比,当员工无法处理某些问题时,就要提交到高层管理去处理。
2、Java 的异常处理语法并不多, try-catch-finally, throw, throws,关键在于异常的合理使用:何时使用异常;如何正确使用异常;当异常发生时,要确保程序处于正确稳定的状态下。You should always ask : when exception happens , will everything properly cleanup?
3、异常处理机制的益处:将正确情形代码与错误处理相分离。
遵循传统错误处理方式的代码是:
if1 (! Exception1) { ifN (! ExceptionN) { |
使用异常处理的代码是:
try { // 正确情形代码1 …… // 正确情形代码N } catch (Exception1) { // 错误情形代码1 } …… } catch (ExceptionN) { // 错误情形代码N } finally { // 清理资源,或使程序回复某个状态 } |
4、自定义异常的方法很简单,让它继承自Java 中的已有异常,比如 Exception . 自定义异常的一个重要事项是异常类的名字必须取好,望文生义,具有自描述性。因为,错误源的信息提示是非常重要的。
5、异常的重要继承体系: RuntimeException ---> Exception --- > Throwable , Error ---> Throwable . 其它异常均是继承于此。异常的重要方法有: getClass().getName() [获取异常类名];getMessage() , getLocalizedMessage() [异常信息]; new SomeException(String), new SomeException(Throwable)[构造器]; fillInStackTrace(), getCause() [用于重抛异常];initCause(Throwable) [异常链] ;printStackTrace() , printStackTrace(PrintStream) , printStackTrace(PrintWriter) [打印异常发生栈调用信息] 。
6、Java 异常链:将刚刚捕获的异常对象作为构造器参数传入将要抛出的异常对象,或者作为 initCause() 方法的参数传入,可以使即将抛出的异常保存有刚刚捕获的异常对象的信息,构成异常链,用来表达因果关系。
7、若类的方法 f 中抛出异常A1,A2,...,An,则该方法的声明必须指定异常声明 f() throws A1, A2, ..., An (A1, A2, ..., An 若是 RuntimeException 或其派生类异常,则可以不指定。)
8、异常占位或预留技术: 方法中并不抛出异常,但方法中仍然指定该异常声明 。这样做的好处时,当真正需要在方法中抛出该异常时,不会影响客户代码;因为客户代码已经对该异常进行处理了(在没有实际抛出该异常之前)。
9、异常声明限制:
(1)子类构造器的异常声明列表必须是基类构造器异常声明列表的超集,即:若基类构造器的异常声明列表是 {A1,A2,...,An} , 则子类构造器的异常声明列表 S >= {A1,A2,...An} ; 这是因为,调用子类构造器之前必然调用基类构造器,因此,子类构造器必须处理基类构造器所声明的所有异常;
(2)子类覆写方法的异常声明列表必须是基类方法异常声明列表的子集,即:若基类方法的异常声明列表是 {A1,A2,...,An} , 则子类覆写方法的异常声明列表 S <= {A1,A2,...An} ; 这是因为, 客户代码是基于基类方法提供的公共接口来编程的,只应当处理基类方法提供的公共接口所声明的异常。
(3)子类型的接口实现方法的异常声明列表必须是接口方法异常声明列表的子集。这是因为,客户代码是基于接口方法的声明来编程的,只应当处理接口方法提供的异常说明。