程序的两个衡量标准:
--->正确性(程序员的代码能实现正确的功能)
--->健壮性(当发生意外状况【参数错误,停电等】,程序能安全,准确的退出,不会产生意想不到的结果)
影响程序健壮性的坏蛋
--->缺陷(物理缺陷[硬件],代码缺陷[代码漏洞])
--->错误(代码bug)
--->失效,失败(方法不能达到目的)
--->异常,例外(错误的运行参数或错误的运行环境)
面向对象的语言中设计异常处理机制需要考虑的是个因素
--->表达:如何表达异常?
--->声明:往外传递的异常是否需要声明?
--->通知:如何产生一个异常实例?
--->传递:如何传递异常?
--->架构:异常处理程序可绑定到何种程序区块?
--->解决:针对异常,如何找到可以处理它的异常处理程序?
--->继续:异常发生后,程序的控制流程该如何进行?
--->清理:无论是否发生异常,如何让程序清理资源,以保持正常的状态?
--->可靠性检查:因为异常处理机制所造成的问题,程序语言提供何种检查?
--->并发性:程序语言对于并行处理程序,提供多少异常处理的支援?
一:表达
【1】程序语言表达异常的方法有以下三种:
==>符号:用字符串或整数来表达异常。防止因为相表达错误造成的系统性能下降。
==>数据对象:用对象表示与存储错误信息,这是最常见的异常表达方式。异常对象基本上无行为方法,只是单纯以对象形式供数据使用
==>完整对象:除了用对象表示与存储错误消息,甚至连如何产生一个异常实例,传递异常,异常发生之后的程序控制流程等,全都定义在异常类中。
二:声明
【1】函数向外传递异常,是否需要声明在该函数接口之上。有以下几种
==>强制性:所有需要往外传递的异常一定要声明在函数接口上。java的【检查异常】采用强制声明。例子:
package com.yeepay.sxf.exception;
import java.io.IOException;
/**
* 接口
* 异常:内部异常Error(断电,资源不足等等,程序员通过代码处理不了的)
* 外部异常Exception(程序员可以通过代码规避这些异常的)
*
* Exception异常分为运行时异常(非检查异常)
* 编译时异常(检查异常)
* @author sxf
*
*/
public interface MyBookService {
/**
* 所有需要往外层传递的异常必须声明在接口上
* 调用该实现方法的方法需要处理该异常(或抛或抓)
* @return
* @throws IOException
*/
public String getBook() throws IOException ;
}
==>选择性:声不声明都可以,例如java的【非检查异常】就可以不用在接口方法上声明,即使声明了也不会有错。
/**
* 非检查异常,可在接口方法声明,也可不声明。
* @throws IOException
*/
public static void test01() {
throw new IllegalArgumentException();
}
==>不支持:根本不支持在接口上声明异常。例如C#语言,没有关键字来支持异常声明。开发人员只能用注释说名该方法会丢出什么异常。
==>混合式:同时支持上述一种以上的声明方式。广义而言,java对于异常是否需要声明的做法是属于混合式的作法。因为只有【检查异常】是要强制声明的。【非检查异常】则选用的是选择式声明的方式。
三:通知
【1】把异常实例传递给异常接收者的这个动作叫做发送通知。从发送通知的角度,异常产生的方式可分为以下两种形式。
==>同步异常:因程序中所调用的指令或函数执行失败所产生的异常
==>异步异常:由执行环境(jvm)主动丢出的异常,和目前程序所执行的指令或函数没有直接关系。例如,执行环境检测到内部错误或是内存不足而丢出的一个异常,像是java的OutOfMemoryError就属于异步异常。
【2】大部分的异常处理设计都在谈论的是如何处理同步异常。因此,当异步异常发生时,通常表示执行环境已经产生严重的错误。此时最好的办法就是结束程序执行,避免一错再错。若是从异常是否可传递出软件组件的接口。又可将异常分为两种
==>内部异常:在软件组件,方法内部产生的异常,主要的目的是将程序执行控制权跳转到一个内部定义的异常处理程序。内部异常无法传递软件组建外部。Catch住的异常。
==>外部异常:在软件组件,方法内部无法处理的异常,需要向外部传递。throws出去的异常。
四:传递
【1】现在讨论异常的传递问题。例如A函数调用B函数,在B函数内部产生一个异常,但这个异常没有被B函数处理Catch住,则该异常可能会被传递到A函数之上。
异常的传递方式分两种
==>显式:(1)在接口上声明往外抛的异常(检查异常或非检查异常)(2)方法内部Catch住调用方法的异常,转化成别的异常抛出去。
/**
* 非检查异常,可在接口方法声明,也可不声明。
* @throws IOException
*/
public static void test01() {
throw new IllegalArgumentException();
}
/**
* 当抓出异常,处理后,转换成客户端认识的异常,往外抛
* @throws Exception
*/
public static void test02() throws Exception{
try {
test01();
} catch (IllegalArgumentException e) {
e.printStackTrace();
throw new Exception();
}
==>隐式:又称为"自动传递方式"。接收到异常的方法,如果不想处理这个异常,在默认情况下,异常便会直接往外传递,不需要特意指定要把这个未处理的异常往外丢。java的【非检查异常】就属于隐式传递异常的。
五:架构
【1】java中异常处理的架构为Try{}catch{}finaly{}语句。此处就不赘言。
六:解决处理
【1】异常处理又称之为程序绑定,探讨如何找到一个异常处理程序的过程。寻找处理程序可以通过以下几种方式。
==>静态范围:从代码结构上,就知道需要如何处理。这个是Catch住异常,catch块中进行处理。
==>动态范围:多个catch块,每个catch块捕获的异常是不一样的。相应的异常,由相应的catch块中的程序进行处理
==>半动态范围:纯粹采用静态或动态范围决定如何寻找。
==>堆栈展开:程序执行的时候,函数调用的顺序形成一个调用链,而这个调用链通常被执行环境保存在一个堆栈中。堆栈展开的意思是,沿着这个堆栈一层一层地往外找,直到找到一个合适的异常处理程序。
==>堆栈切割:另外一种寻找方式则是把异常处理程序放在一个list里面。当异常发生时,便到这个list中找是否有合适的异常处理程序。
七:继续
【1】异常处理又称异常模型,规范的是当异常处理程序执行完毕返回控制权之后,程序的执行流程。常见的异常模型包含以下三种
==>终止模型:异常发生点,未标记。所catch的块代码执行完毕,则不会从异常发生点之后开始继续执行,而是从catch块之后开始执行代码。因此java的继续,为终止模型。
==>恢复模型:执行完异常处理程序之后,会继续执行原本发生异常的那一行指令。Visual Basic就支持这一种模型。当异常处理完毕之后,可以用resume或是resume next指令让过程控制权返回异常发生的那一行或是下一行程序。
==>重试模型:是终止模型和恢复模型结合产生的。
八:清理
【1】无论是否发生异常,一个软件的组件都应该保持正确的状态。清理动作包含在错误发生之后,让系统恢复到一个可以接受一致性状态或释放资源以免资源耗尽。
==>显示传递:当异常往外传递之前,系统要调用资源清理程序代码。
==>特定构造:采用特定构造确保无论是否有异常发生,资源清理的程序代码都会被执行。java的finally块中放清理资源的代码。
==>自动清理:系统自动清理所有资源。程序设计完全不需要关心资源释放的问题。目前似乎没有主流的程序语言做到这一点。java的try-with-resources与c#的using,勉强算是比较接近自动清理的做法
九:可靠性检查
【1】异常处理也是一种程序设计。因此,可能会因为程序语言的异常处理机制发生错误。可靠性检查,分以下两种
==>静态检查:就是编译器的检查。检查你的异常处理的代码,是否符合语言语法。
==>动态检查:靠程序员自身的能力,对代码的把控。进行相应的检查。
十:并发性。
【1】并行或并发算是程序语言里面比较高端的项目。如果异常处理有考考虑到并发处理,则允许同时间可有好几个异常一起发生。而异常处理机制也要考虑如何处理这些同时产生的异常状况。程序语言对与并行处理的异常,可分为以下三种
==>不支持:老牌的程序语言C++,没有内建并行处理的支持,更谈不上在并行处理程序中的异常处理机制
==>有限支持:可以在多线程的程序中丢出异常,但是异常处理程序的解析离不开程序员的规划,系统也不支持“不可分割的动作”
==>完整支持:对于并行处理的程序的异常处理具有最完整的支持(理想),程序语言可以制定那些函数是属于不可分割动作,当执行失败的时候,异常处理机制确保这些不可分割的动作状态完整性。只要是与并行处理程序特性相关的异常状况,基本上都支持。