Atitit.异常的设计原理与 策略处理 java 最佳实践 p93
Atitit.异常的设计原理与 策略处理 java 最佳实践 p93
1.1 普通项目优先使用异常取代返回值,如果开发类库方面的项目,最好异常机制与返回值都提供,由调用者决定使用哪种方式。。2
1.2 优先把异常抛出到上层处理。。异常本身就是为了方便把异常情况抛出到上层处理。。2
1.3 对于 HYPERLINK \l _Toc6222 方法调用结果异常情况返回策略,最终会有四种策略状况,2
2.2 用throw抛出一个异常到catch子句中与通过函数调用传递一个参数两者基本相同。 4
2.4 RuntimeException跟checked Exception 4
2.7 Base类and 扩展class 抛出的特别的异常不一样的解决之道5
5.1 下面是JDK API中列出的异常类: 除了RuntimeException以外的,都是checked Exception10
1 异常方面的使用准则,答案是::
1.1 普通项目优先使用异常取代返回值,如果开发类库方面的项目,最好异常机制与返回值都提供,由调用者决定使用哪种方式。。
1.2 优先把异常抛出到上层处理。。异常本身就是为了方便把异常情况抛出到上层处理。。
当然根据一些特殊情况可以还有其他方式来处理。。但并非将来的主流趋势...需要注意。。
作者:: 老哇的爪子 Attilax 艾龙, EMAIL:1466519819@qq.com
转载请注明来源: http://www.cnblogs.com/attilax/
1.3 对于方法调用结果异常情况返回策略,最终会有四种策略状况,
抛出异常excepttion、返回特殊值、阻塞、超时2
1.4 返回null 还是异常??
·
对于极其常见的错误案例,返回 null 而不是引发异常。 极其常见的错误案例可被视为常规控制流。 通过在这些情况下返回 null,可最大程度地减小对应用的性能产生的影响。
2 异常的由来与设计
任务与预先设定的规则不相符的情况都可以称之为异常。但凡业务逻辑操作,都会划定一些边界或规则,但是往往事与愿违,总会有调皮鬼来挑战系统的健壮性。这些调皮鬼包括:
1、系统用户。用户并不都是知道潜规则的,比如用户的银行账户中只有100块钱,但是用户不查询就直接取200块。
2、开发人员。码农有时候太过自信了,比如你在编写文件下载功能时忽略了文件有可能不存在这个分支流程。
3、运行环境。软件系统也是要靠天吃饭的,谁都保不准网络一直畅通,硬盘一直稳定。
2.1 为什么使用异常
对C程序来说,使用Error Code就可以了,为什么还要引入异常?因为异常不能被忽略。如果一个函数通过设置一个状态变量或返回错误代码来表示一个异常状态,没有办法保证函数调用者将一定检测变量或测试错误代码。结果程序会从它遇到的异常状态继续运行,异常没有被捕获,程序立即会终止执行。
在C程序中,我们可以用int setjmp( jmp_buf env );和 void longjmp( jmp_buf env, int value );这2个函数来完成和异常处理相识的功能,但是MSDN中介绍了在C++中使用longjmp来调整stack时不能够对局部的对象调用析构函数,但是对C++程序来说,析构函数是重要的(我就一般都把对象的Delete放在析构函数中)。 <br>所以我们需要一个方法:①能够通知异常状态,又不能忽略这个通知,②并且Searching the stack以便找到异常代码时,③还要确保局部对象的析构函数被Call。而C++的异常处理刚好就是来解决这些问题的。</p> <p>有的地方只有用异常才能解决问题,比如说,在当前上下文环境中,无法捕捉或确定的错误类型,我们就得用一个异常抛出到更大的上下文环境当中去。还有,异常处理的使用呢,可以使出错处理程序与“通常”代码分离开来,使代码更简洁更灵活。另外就是程序必不可少的健壮性了,异常处理往往在其中扮演着重要的角色。</p>
因为当函数返回时局部对象总是被释放,无论函数是如何退出的。(仅有一种例外就是当你调用longjmp时。Longjmp的这个缺点是C++率先支持异常处理的主要原因)
尽管C++首次引入异常的规范
尽管C++首次引入异常的规范,但是Java是强制实施"检查的异常"(Checked Exception)规范的唯一的主流语言
2.2 用throw抛出一个异常到catch子句中与通过函数调用传递一个参数两者基本相同。
这里面确有一些相同点,但是他们也存在着巨大的差异。</p> <p>让我们先从相同点谈起。你传递函数参数与异常的途径可以是传值、传递引用或传递指针,这是相同的。但是当你传递参数和异常时,系统所要完成的操作过程则是完全不同的。产生这个差异的原因是:你调用函数时,程序的控制权最终还会返回到函数的调用处,但是当你抛出一个异常时,控制权永远不会回到抛出异常的地方。</p>
2.3 S E H的主要动机
微软在Wi n d o w s中引入S E H的主要动机是为了便于操作系统本身的开发。操作系统的开发人员使用S E H,使得系统更加强壮。我们也可以使用S E H,使我们的自己的程序更加强壮。 <br>使用S E H所造成的负担主要由编译程序来承担,而不是由操作系统承担。 <br>当异常块(exception block)出现时,编译程序要生成特殊的代码。编译程序必须产生一些表( t a b l e)来支持处理S E H的数据结构。 <br>编译程序还必须提供回调( c a l l b a c k)函数,操作系统可以调用这些函数,保证异常块被处理。 <br>编译程序还要负责准备栈结构和其他内部信息,供操作系统使用和参考。 <br>在编译程序中增加S E H支持不是一件容易的事。不同的编译程序厂商会以不同的方式实现S E H,这一点并不让人感到奇怪。幸亏我们可以不必考虑编译程序的实现细节,而只使用编译程序的S E H功能。(其实大多数编译程序厂商都采用微软建议的语法)
2.4 RuntimeException跟checked Exception
Java的Exception分为两类,一类是RuntimeException及其子类,另外一类就是checked Exception。Java要求函数对没有被catch处理掉的checked Exception,需要将其写在函数的声明部分
.net 只有RuntimeException
除了Error与RuntimeException,其他剩下的异常都是你需要关心的,而这些异常类统称为Checked Exception,至于Error与RuntimeException则被统称为Unchecked Exception.
2.5 要不要使用checked Exception
要使用....优点是流程clr,and ide能自动生成结构代码...
中建议在遇到可恢复的错误时采用checked异常,遇到不可恢复的异常时采用unchecked异常。
2.6 checked Exception 的缺点
支持Unchecked异常:
沿调用栈向上传播的Checked异常破坏了顶层的方法,因为这些方法必须声明抛出所有它们调用的方法抛出的异常
Check异常的抛出作为方法接口的一部分,这使得添加或移除早期版本中方法的异常难以实现。
2.7 Base类and 扩展class 抛出的特别的异常不一样的解决之道
可以在Base类的foo方法中加入抛出ExceptionB的声明,然而,这样就破坏了open-close原则。而且,有时我们没有办法去修改父类,比如当重载一个Jdk里的类的时候。
另一个可能的做法是在Extend的foo方法中catch住ExceptionB,然后构造一个ExceptionA并抛出。这是个可行的办法但也只是一个权宜之计。
2.8 【异常处理框架】
有问题我们就要解决问题,如果问题解决不了,那么就把问题的影响面降到最低。对于异常也是如此,为了提高健壮性,需要对于异常进行兼容。处理异常一个很重要的原则是不逃避,不歪曲。吞异常与提供错误的异常信息一样罪恶。对于异常的处理框架可以大致分为以下几点:
1、异常类封装。根据异常产生的原因,我们把异常封装为两类,分别是业务逻辑异常(BusinessException)和系统编程异常(ProgramException),分类的目的是进行分类处理。
2、异常检测类。这是产生异常的地方,包括了业务逻辑检测(对于不符合业务逻辑的情况封装异常抛出),还包括了对于底层异常的转化封装(比如将数据库操作产生的异常捕获并封装为ProgramException)。
3、异常处理类。这是异常的终点,在此异常会以一种比较合适的方式对系统运维人员和系统的用户进行优雅的展示(比如记录日志或者页面提示)。
三、异常链
对于封装和转换的异常应该保留最底层原始的错误信息,即 root cause。只要向外抛出异常,就应该讲 root cause 异常抛出,这样最上层的异常就可以轻松获取底层异常信息。
在自定义异常体系的时候可以直接让异常存在支持异常链的构造器,对于不支持异常的异常,可以利用 Throwable 的 initCause() API 设置。
3 Exception业务流程控制 可以借鉴一下)
在使用UseCase来描述一个场景的时候,有一个主事件流和n个异常流。异常流可能发生在主事件流的过程,而try语句里面实现的是主事件流,而 catch里面实现的是异常流,在这里Exception不代表程序出现了异常或者错误,Exception只是面向对象化的业务逻辑控制方法。如果没有 明白这一点,那么我认为并没有真正明白应该怎么使用Java来正确的编程。
而我自己写的程序,会自定义大量的Exception类,所有这些Exception类都不意味着程序出现了异常或者错误,只是代表非主事件流的发生的, 用来进行那些分支流程的流程控制的。例如你往权限系统中增加一个用户,应该定义1个异常类,UserExistedException,抛出这个异常不代 表你插入动作失败,只说明你碰到一个分支流程,留待后面的catch中来处理这个分支流程。传统的程序员会写一个if else来处理,而一个合格的OOP程序员应该有意识的使用try catch 方式来区分主事件流和n个分支流程的处理,通过try catch,而不是if else来从代码上把不同的事件流隔离开来进行分别的代码撰写
·
4 4. 常见的捕获异常后的处理策略2
从开发效率,灵活性考虑。。可以根据情况适当转换异常(不要使用返回值),继续抛出到sevlet层,这样可以::
方便aop filter对异常做处理。分层处理。
可以容易实现配置化的异常处理。。
4.1 check异常转换为runtime 异常
当check异常转换为runtime 异常时候的判断异常类型。。如果已经是re了,就不要转换了
try {
if(isOvertime_3month(last_check_time))
{
try {
String pcid=getPCid();
PropX px=new PropX(pathx.webAppPath()+"/0cfg/cfg.properties");
String url=px.getProperty("timer_check_authcode_url");
Map m=new HashMap();
m.put("pc_id", pcid);
url=new ParamX().varReplace(url, m, "@");
url=new ParamX().varReplace(url, px.getValList(), "%");
System.out.println(url);
String now_check_time=websitex.WebpageContent(url);
pref.put("now_check_time", now_check_time);
} catch (Exception e) {
throw new OverTimeEx(e.getMessage());
}
}
} catch (Exception e) {
if (e.getCause() instanceof java.text.ParseException) {
throw new RuntimeException("ret from php date fmt err:"
+ e.getMessage());
}
if (e instanceof RuntimeException) {
throw (RuntimeException) e;
} else
throw new RuntimeException(e);
}
4.2 checked Exception 转换 re
为了避免在函数声明中写throws部分,在Java项目里面常常可以看到以下代码用来‘吞掉’Exception:
4.3 丢出新异常D,可以让它从已有的异常中继承,
[iii] 在“The Design and Evolution of C++”, Bjarne Stroustrup也提到,同样也基于对这个问题的考虑,c++没有“Static Checking”(checked exceptions)而是采用“Run time Checking”。而且Stroustrup建议,对于丢出新异常D,可以让它从已有的异常中继承,这样既不影响已有代码,新的代码也可以处理它。(这是Stroustrup在1990就作出的结论!)
4.4 常见的捕获异常后的处理策略
4.5 转换为业务异常,抛出至上层处理(常用)
从通信层异常转为业务异常,方便理解
主要是业务层处理与view层处理
一般是 catch 到 Lower Level Exception,但是向外抛出的却是 Higher Level Exception,对异常进行转换。
4.6 根据异常种类处理异常(常用)
4.7 事务rollback
能 rollback 的尽量 rollback
4.8 日志记录,重新抛出
主要用来统计分析稳定性情况,预警等
4.9 忽略异常(较少这样处理)
为了提升稳定性,需要冗余处理的时候,可以这样做。。。
5 列举最常用的五种RuntimeException:
这是JAVA认证考试中最常见的题目,事实上,runtime exception中最常见的,经常碰到的,也就5,6种,如下:
ArithmeticException |
int a=0; |
ClassCastException: |
Object x = new Integer(0); |
IndexOutOfBoundsException |
int [] numbers = { 1, 2, 3 }; |
IllegalArgumentException |
int a = Interger.parseInt("test"); |
NullPointerExceptionextends |
|
· 除了RuntimeException,其他继承自java.lang.Exception得异常统称为Checked Exception,他们有多少种呢?
5.1 下面是JDK API中列出的异常类:
除了RuntimeException以外的,都是checked Exception
java.lang.Object |
5.2 Java异常类的层次结构
图中红色部分为受检查异常。它们必须被捕获,或者在函数中声明为抛出该异常。
6 参考
Anders Hejlsberg论为什么不在c#引入类似java的checked exceptions - 产品和技术 - 赛迪网.htm
C++处理异常技巧.htm
[转载]JAVA 的checked异常和unchecked异常_4527_新浪博客.htm
Atitit.部分错误异常处理框架atiPartErr 的总结.doc
Atitit.常见的异常处理策略总结.doc
Atitit.java 异常的使用总结最佳实践 p56.doc (frm Vo8f)
Atitit.常见的捕获异常后的处理策略总结