- 浏览: 1512584 次
- 性别:
- 来自: 杭州
文章分类
最新评论
-
duxingzheZet:
listview.getScrollY(); // 为啥总是0 ...
Android 记录和恢复ListView滚动的位置的两种方法 -
Escalade:
AlertDialog显示错误 Unable to add window token null is not for an application -
陌路千里:
return false即可
form表单只提交值,而不进行页面跳转 -
yubenjie:
不错,说的很全。学习了
maven用途、核心概念、用法、常用参数和命令、扩展 -
fxiaozj:
中文加数字还是不行呀
解决TextView在中文和标点存在情况下一行没显示满就换行问题
之前一直对异常处理比较疑惑,通常有点随心所欲而没有规范统一的处理标准。这次的项目提供全网的基础服务,认真学习了下异常处理的规范,正好看到了下面的文章,相见恨晚
原文如下,主要需要掌握,分清Checked exception和Unchecked exception;保持封装性统一对外提供异常; 不做没必要的封装处理。
使用时记得finally释放资源; 不要使用异常作控制流程之用; 处理不了的异常不要狗拿耗子,要么catch小粒度exception要么继续向上抛。
译者注:这是一篇2003年的文章,因为时间久远,可能有些观点已经过时,但里面讨论的大部分方法如今仍能适用。如若有其他好的错误处理的方法,欢迎留言。
异常处理的关键就在于知道何时处理异常以及如何使用异常。这篇文章,我会提到一些最佳的异常处理方法。我也会总结checked exception的用法。
我们程序员都想写出高质量的代码来解决问题。但是,异常有时会给我们的代码带来副作用。没有人喜欢副作用,所以我们很快找到了方法来改善它们。我看见过许多聪明的程序员这样来处理异常:
1
2
3
4
5
6
7
|
public void consumeAndForgetAllExceptions(){
try {
...some code that throws exceptions
} catch (Exception ex){
ex.printStack trace ();
}
} |
上面的代码有什么错误?
当异常被抛出后,正常的程序执行过程中断,控制权交给catch段,catch段会catch 异常,然后抑制异常的进一步扩大。然后接着catch段之后程序继续执行,好像什么都没发生过一样。
下面的代码呢?
1
2
|
public void someMethod() throws Exception{
} |
这个方法内没有代码,是个空方法。一个空方法怎么能抛出异常呢?Java并没有说不让这么做。最近,我遇到过类似的代码,方法抛出了异常,而其中的代码实际上并不产生那个异常。当我问这个程序员为何要这么做,他回答道“我知道,虽然这样做破坏了API,但我习惯这么做,而且这样也可行。”
C++社区用了许多年才确定如何使用异常机制。这个争论刚刚在Java社区展开。我见到一些Java程序员正在和异常进行顽强抗争。如果用法不当的话,会拖慢程序,因为创建、抛出和接住异常都会占用内存。如果过多的使用异常的话,代码会变得很难阅读,对要使用API的程序员来说无疑会增加挫败感。我们知道挫败感会令我们写出很烂的代码。有的程序员会刻意回避这个问题,忽略异常或随意抛出异常,就像上面的两个例子一样。
异常的本质
广义的讲,抛出异常分三种不同的情况:
- 编程错误导致的异常:在这个类别里,异常的出现是由于代码的错误(譬如NullPointerException和IllegalArgumentException)。代码通常对编程错误没有什么对策。
- 客户端的错误导致的异常:客户端代码试图违背制定的规则,调用API不支持的资源。如果在异常中显示有效信息的话,客户端可以采取其他的补救方法。例如:解析一个格式不正确的XML文档时会抛出异常,异常中含有有效的信息。客户端可以利用这个有效信息来采取恢复的步骤。
- 资源错误导致的异常:当获取资源错误时引发的异常。例如,系统内存不足,或者网络连接失败。客户端对于资源错误的反应是视情况而定的。客户端可能一段时间之后重试或者仅仅记录失败然后将程序挂起
Java异常的类型
Java定义了两种异常
- Checked exception: 继承自Exception类是checked exception。代码需要处理API抛出的checked exception,要么用catch语句,要么直接用throws语句抛出去。
- Unchecked exception: 也称RuntimeException,它也是继承自Exception。但所有RuntimeException的子类都有个特点,就是代码不需要处理它们的异常也能通过编译,所以它们称作unchecked exception。
图1显示了NullpointerException的继承级别。
NullpointerException继承自RuntimeException,所以它是个unchecked exception。
我看到人们大量使用checked exception的,而很少看到unchecked exception的使用。近来,在Java社区里对checked exception和它的真正价值的争论愈演愈烈。这主要因为Java是第一个使用checked exception的主流面向对象语言。C++和C#都没有checked exception,所有的异常都是unchecked。
低层次抛出的checked exception对高层次来说,必须要catch或者throw它们。这样如果不能有效处理异常的话,checked exception就在API和代码之间造成了一直负担。程序员就开始写一些空的catch代码段,或者仅仅抛出异常,实际上,给客户端的触发者来说增加了负担。
Checked exception也被诟病破坏了封装性。看看下面的代码:
1
2
3
4
|
public List getAllAccounts() throws
FileNotFoundException, SQLException{
...
} |
getAllAccounts()抛出了两个checked exception。这个方法的调用者就必须处理这两个异常,尽管它也不知道在getAllAccounts中什么文件找不到以及什么数据库语句失败,也不知道该提供什么文件系统或者数据库的事务层逻辑。这样,异常处理就在方法调用者和方法之间形成了一个不恰当的紧耦合。
设计API的最佳实践
说了这么多,让我们来说说如何设计一个好的API,能够正确抛出异常的。
1. 当要确定是使用checked exception还是unchecked exception时,首先问问自己,当异常发生时客户端如何应对?
如果客户端可以从异常中采取行动进行恢复的,就使用checked exception,如果客户什么也做不了,就用unchecked exception。我指的是,不仅仅是记录异常,还要采取措施来恢复。
还有,我更喜欢unchecked exception,因为不需要强迫客户端API必须处理它们。它们会进一步扩散,直到你想catch它们,或者它们会继续扩散爆出。Java API有许多unchecked exception如NullPointerException, IllegalArgumentException和IllegalStateException。我更愿意用这些Java定义好的异常类,而非我们自己创建的异常类。它们使我们的代码易读,也避免代码消耗更多内存。
2. 保持封装性
不要将针对某特定实现的checked exception用到更高的层次中去。例如,不要让SQLException扩散到逻辑层去。因为逻辑层是不需要知道SQLException。你有两种选择:
- 如果你的客户端有应对措施的话,将SQLException转化成另一个checked exception。
- 如果你的客户端什么也做不了的话,将SQLException转化成一个unchecked exception。
但大部分情况是,客户端对SQLException无能为力。那请将SQLException转换成unchecked exception吧。来看下面的代码:
1
2
3
4
5
6
7
|
public void dataAccessCode(){
try {
..some code that throws SQLException
} catch (SQLException ex){
ex.printStack trace ();
}
} |
上面的catch段仅仅抑制了异常,什么也没做。这是因为客户针对SQLException无计可施。何不使用下面的方法呢?
1
2
3
4
5
6
7
|
public void dataAccessCode(){
try {
..some code that throws SQLException
} catch (SQLException ex){
throw new RuntimeException(ex);
}
} |
将SQLException转换成RuntimeException。如果SQLException发生时,catch语句抛出一个新的RuntimeException异常。正在执行的线程会挂起,异常爆出来。然而,我并没有破坏逻辑层,因为它不需要进行不必要的异常处理,尤其是它根本不知道怎么处理SQLException。如果catch语句需要知道异常发生的根源,我可以用getCause()方法,这个方法在JDK1.4中所有异常类中都有。
如果你确信逻辑层可以采取某些恢复措施来应对SQLException时,你可以将它转换成更有意义的checked exception。但我发现仅仅抛出RuntimeException,大部分时间里都管用。
3. 如果自定义的异常没有提供有用的信息的话,请不要创建它们。
下面的代码有什么错误?
1
2
|
public class DuplicateUsernameException
extends Exception {}
|
它没有给出任何有效的信息,除了提供一个异常名字意外。不要忘了Java异常类就像其他的类一样,当你在其中增加方法时,你也可以调用这些方法来获得更多信息。
我们可以在DuplicateUsernameException中增加有效的方法,例如:
1
2
3
4
5
6
7
|
public class DuplicateUsernameException
extends Exception {
public DuplicateUsernameException
( String username){....}
public String requestedUsername(){...}
public String [] availableNames(){...}
} |
新版本的DuplicateUsernameException提供两个方法:requestedUsername()返回请求的姓名,availableNames()返回与请求姓名相类似的所有姓名的一个数组。客户端可以知道被请求的姓名已经不可用了,以及其他可用的姓名。如果你不想获得其他的信息,仅仅抛出一个标准的异常即可:
1
|
throw new Exception( "Username already taken" );
|
如果你认为客户端不会采取任何措施,仅仅只是写日志说明用户名已存在的话,抛出一个unchecked exception:
1
|
throw new RuntimeException( "Username already taken" );
|
另外,你甚至可以写一个判断用户名是否已经存在的方法。
还是要重复一遍,当客户端的API可以根据异常的信息采取有效措施的话,我们可以使用checked exception。但对于所有的编程错误,我更倾向于unchecked exception。它们让你的代码可读性更高。
4. 将异常文档化
你可以采用Javadoc’s @throws标签将你的API抛出的checked和unchecked exception都文档化。然而,我更喜欢写单元测试。单元测试可看作可执行的文档。无论你选择哪一种方式,都要让客户端使用你的API时清楚知道你的API抛出哪些异常。下面是针对IndexOutOfBoundsException的单元测试:
1
2
3
4
5
6
7
|
public void testIndexOutOfBoundsException() {
ArrayList blankList = new ArrayList();
try {
blankList. get ( 10 );
fail( "Should raise an IndexOutOfBoundsException" );
} catch (IndexOutOfBoundsException success) {}
} |
当调用blankList.get(10)时,上面的代码会抛出IndexOutOfBoundsException。如果不是如此的话,fail(“Should raise an IndexOutOfBoundsException”)会显式的让测试失败。通过写单元测试,你不仅记录了异常如何运作,也让你的代码变得更健壮。
使用异常的最佳实践
下面的部分我们列出了客户端代码处理API抛出异常的一些最佳实现方法。
1. 记得释放资源
如果你正在用数据库或网络连接的资源,要记得释放它们。如果你使用的API仅仅使用unchecked exception,你应该用完后释放它们,使用try-final。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
public void dataAccessCode(){
Connection conn = null ;
try {
conn = getConnection();
..some code that throws SQLException
} catch (SQLException ex){
ex.printStack trace ();
} finally {
DBUtil.closeConnection(conn);
}
} class DBUtil{
public static void closeConnection
(Connection conn){
try {
conn.close();
} catch (SQLException ex){
logger.error( "Cannot close connection" );
throw new RuntimeException(ex);
}
}
} |
DBUtil是一个关闭连接的工具类。最重要的部分在于finally,无论异常发不发生都会执行。在这个例子中,finally关闭了连接,如果关闭过程中有问题发生的话,会抛出一个RuntimeException。
2. 不要使用异常作控制流程之用
生成栈回溯是非常昂贵的,栈回溯的价值是在于调试。在流程控制中,栈回溯是应该避免的,因为客户端仅仅想知道如何继续。
下面的代码,一个自定义的异常MaximumCountReachedException,用来控制流程。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
public void useExceptionsForFlowControl() {
try {
while ( true ) {
increaseCount();
}
} catch (MaximumCountReachedException ex) {
}
//Continue execution
} public void increaseCount()
throws MaximumCountReachedException {
if (count >= 5000 )
throw new MaximumCountReachedException();
} |
useExceptionsForFlowControl()使用了一个无限的循环来递增计数器,直至异常被抛出。这样写不仅降低了代码的可读性,也让代码变得很慢。记住异常仅用在有异常发生的情况。
3. 不要忽略异常
当一个API方法抛出checked exception时,它是要试图告诉你你需要采取某些行动处理它。如果它对你来说没什么意义,不要犹豫,直接转换成unchecked exception抛出,千万不要仅仅用空的{}catch它,然后当没事发生一样忽略它。
4. 不要catch最高层次的exception
Unchecked exception是继承自RuntimeException类的,而RuntimeException继承自Exception。如果catch Exception的话,你也会catch RuntimeException。
1
2
3
4
|
try {
.. } catch (Exception ex){
} |
上面的代码会忽略掉unchecked exception。
5. 仅记录exception一次
对同一个错误的栈回溯(stack trace)记录多次的话,会让程序员搞不清楚错误的原始来源。所以仅仅记录一次就够了。
总结
这里是我总结出的一些异常处理最佳实施方法。我并不想引起关于checked exception和unchecked exception的激烈争论。你可以根据你的需要来设计代码。我相信,随着时间的推移,我们会找到些更好的异常处理的方法的。
转载自:http://blog.jobbole.com/18291/
英文原文:http://onjava.com/pub/a/onjava/2003/11/19/exceptions.html
发表评论
-
Android 开源库获取途径整理
2014-08-20 02:14 3440最新内容请见原文: http://www.trinea.c ... -
maven常用插件配置和使用
2012-05-17 20:14 6406最新内容见:Maven常用插件配置和使用 本文主要是 ... -
总结的一些Java公用函数库
2012-06-18 15:41 9172最新内容建议直接访问原文:Android常用的工具类 ... -
PreloadDataCache支持预取的数据缓存,使用简单,支持多种缓存算法,支持不同网络类型,扩展性强
2012-06-18 15:44 4408最新内容建议直接访 ... -
Junit单测代码中java序列化失败的解决
2012-05-17 00:38 3576本文主要介绍在Junit单元测试中序列化时出现的java.io ... -
重构#读书笔记#
2012-03-31 10:06 01、要在工程已开始就有很好的设计在不改动几乎不可能,因为代码不 ... -
设计模式
2012-03-12 00:11 0设计模式 Gof的设计模式 -
Java Semaphore用法
2012-03-09 14:15 0参考:http://comeonbabye.ite ... -
大话设计模式 #笔记#1
2012-01-31 23:51 0软件设计原则 1、单一职责原则 2、开放封闭原则 3、依 ... -
eclipse中查看Java源代码
2012-01-16 23:25 6717在eclipse中对于自己的代码可以通过按住Ctrl的同时单击 ... -
多线程使用中的一些好习惯
2012-01-17 00:24 2675本文主要介绍个人在多线程使用和调优中总结一些线程使用比较好的习 ... -
ThreadLocal 介绍及使用举例
2012-01-05 21:41 0参考: http://lavasoft.blog.51cto ... -
腾讯微博java(android) api
2011-12-15 00:50 12174由于现在腾讯微博提 ... -
java CyclicBarrier CountDownLatch
2011-12-07 23:30 0http://www.iteye.com/topic/7130 ... -
Hudson 插件编写
2011-12-10 00:10 2835Hudson是一个开源的持续集成工具,由于其强大的扩展性(插件 ... -
maven用途、核心概念、用法、常用参数和命令、扩展
2011-12-05 19:15 66124最新内容见:Maven介绍,包括作用、核心概念、用法、常用 ... -
java 利用spring JavaMailSenderImpl发送邮件,支持普通文本、附件、html、velocity模板
2011-11-25 20:59 23956本文主要介绍利用JavaMa ... -
windows机器性能监控
2011-11-24 18:55 1907主要介绍一般windows系统监控,以及Windows Ser ... -
Junit单元测试中获得spring bean
2011-11-23 11:44 14224主要介绍单元测试中获得bean的三种方法,以及各自的优劣。其 ... -
对象数组或list排序及Collections排序原理
2011-11-09 22:42 27855常需要对list进行排序 ...
相关推荐
7. **最佳实践**:分享在AA中使用JavaScript的最佳实践,以确保代码的可读性、可维护性和性能优化。 8. **社区互动**:鼓励观众参与RPA之家的讨论,共同学习和解决实际遇到的问题。 这个视频教程对于那些正在学习...
7. **最佳实践与案例分析**:Syed Pasha可能会分享一些最佳实践,以及他在实际项目中的经验,帮助学习者避免常见陷阱,提高自动化项目的成功率。 8. **社区支持与交流**:视频教程强调了在学习过程中遇到问题时,...
最佳实践可能包括参数化、异常处理、性能优化、日志记录等方面。 4. `DB2 SQL存储过程语法官方权威指南(翻译).mht`:这个文件提供了DB2 SQL存储过程的官方语法参考,可能包括创建、修改和执行存储过程的步骤,以及...
理解Struts的工作原理和最佳实践是必不可少的。 【Spring】Spring框架是企业级应用开发的基石,面试可能围绕IoC(Inversion of Control)和AOP(Aspect-Oriented Programming)展开,包括Bean管理、依赖注入、事务...
9. **最佳实践**:学习良好的编程习惯,如使用PreparedStatement防止SQL注入,释放数据库连接,避免内存泄漏等。 这个实战项目对于初学者来说是一个很好的练习,可以帮助他们更好地理解和掌握Web开发的基本流程,...
7. **最佳实践**:教程可能会分享一些关于如何有效管理数据输出和确保数据安全性的最佳实践,这对于企业级RPA实施至关重要。 8. **集成其他RPA组件**:查看数据表活动通常是RPA流程的一部分,可能还会涉及到如何与...
9. **最佳实践**:教程可能还会提供一些使用通用记录器的最佳实践,比如避免过度依赖记录器、理解自动化流程的潜在风险等。 通过学习这个教程,初学者可以掌握Automation Anywhere的通用记录器功能,并逐步提升RPA...
7. **最佳实践**:教程可能还会分享编写高效、可维护Python脚本的技巧,以及如何将Python脚本与AA的工作流相结合的最佳实践。 通过这个教程,学习者不仅可以提升Python编程技能,还能掌握如何在RPA项目中有效地利用...
10. **最佳实践**:了解在处理CSV文件时的一些最佳实践,例如,如何处理编码问题,如何避免数据丢失,以及如何优化性能。 通过本教程的学习,你不仅可以掌握UiPath中处理CSV文件的基本技巧,还能进一步提升你的RPA...
8. **最佳实践和注意事项**:了解如何优化Twilio活动的使用,避免过度依赖,同时确保遵守通信法规。 通过观看此教程,初学者可以深入理解RPA与通信集成的可能性,而经验丰富的RPA开发者则能进一步提升其自动化解决...
7. **最佳实践**:学习如何编写可维护、可扩展的RPA代码,遵循良好的编程习惯,以提高自动化流程的可靠性。 在学习过程中,配合视频教程,你可以在实践中不断巩固理论知识。同时,通过观看Edureka的专业讲解,你...
它们详细介绍了配置选项、API 使用和最佳实践,帮助开发者快速上手。 在实际项目中,我们通常会结合 Spring Boot 和 Spring Security 进行配置,利用自动配置简化流程。通过阅读 `_SpringSecurity3_教程及官方参考...
8. 遵循最佳实践:最后,学习者会了解到在实施RPA时的一些最佳实践,如何确保合规性,以及如何进行有效的变更管理和风险管理。 总的来说,通过观看这个视频教程,学习者将能够系统地了解RPA,掌握其基本原理和操作...
6. **最佳实践和案例研究**:分享实际项目中的经验和建议,展示RPA在不同业务场景中的应用。 通过这个教程,学习者不仅可以掌握RPA和UiPath的基础知识,还能了解到如何将这些技能应用于实际的Salesforce自动化项目...
综合来看,这个C++ Collection.web资源包旨在深入探讨C++的高级特性,包括异常处理、智能指针的比较、模板的使用、STL的最佳实践,以及C++对其他语言特性(如C#的属性)的实现。这些文章对于希望提高C++编程技能,...
通过本教程的学习,观众不仅能掌握在Enterprise A2019中模拟按键的基本技巧,还能了解到RPA在实际工作中的应用策略和最佳实践,从而提升自身的RPA实施能力。在RPA之家社区,观众还可以与同行交流心得,共同探讨RPA...
Java编程事项是一个涵盖广泛的主题,它包含了众多关于Java语言编程的最佳实践、常见陷阱以及高效编程技巧。这篇由多个来源整理转贴的资料,以.htm格式呈现,旨在帮助开发者提升编程技能,避免常见错误,理解Java的...
8. **最佳实践与案例研究**:可能包含真实世界中的RPA应用案例,帮助学习者更好地理解和应用所学知识。 通过这套教程,无论是初学者还是有一定经验的RPA从业者,都能提升自己的技能水平,为实现企业的数字化转型...
10. **最佳实践和案例研究** - 提供实际使用中的最佳实践,以及一些成功的案例,帮助读者更好地理解和应用这两个工具。 总的来说,这篇文章将为读者提供一个全面的视角,理解jBPM的历史发展,以及在面对新的流程...
6. **最佳实践**:学习已有的成功案例,吸取经验,避免常见错误。 7. **持续优化**:理解RPA项目实施后的监控和维护,以及如何根据反馈进行持续优化。 通过这个视频教程,你不仅可以深入理解RPA技术,还可以了解到...