- 浏览: 329534 次
- 性别:
- 来自: 北京
-
文章分类
- 全部博客 (211)
- Java IO (3)
- Struts (13)
- Hibernate (19)
- Spring (9)
- jQuery (2)
- Extjs (8)
- Flex (1)
- Oracle (4)
- ibatis (3)
- SQL (0)
- WorkFlow (0)
- Java Core (14)
- Ant&Maven (18)
- Java Thread (0)
- Java Socket (1)
- Java OO (2)
- Javascript (14)
- CSS&Div (2)
- MYSQL (3)
- MSSQL (0)
- Design Pattern (3)
- Data Structure&Algorithm (1)
- Source Analysis (0)
- OSGi (3)
- Linux (7)
- Network (1)
- 百无聊赖 (9)
- English (5)
- Japaness (0)
- French (0)
- Webservice (3)
- 考试认证 (0)
- C/C++ (1)
- 小题目 (1)
- Server (1)
- JSP&Servlet (18)
- JDBC (8)
- JMS (3)
- JNDI (0)
- 软件工程 (2)
- 项目管理 (2)
- UML (0)
- 文档翻译 (0)
- 架构之美 (1)
- EJB (0)
- DataBase Design (1)
- DataBase (1)
- Framework Integration (2)
- JPA (2)
- Daily Reading (8)
- 通用组件设计 (3)
- Spring DM (1)
- Spring MVC (0)
- Spring Security (0)
- 时间管理 (0)
- 成本管理 (1)
- 进度管理 (0)
- 质量管理 (0)
- 范围管理 (0)
- 环境配置 (5)
- 敏捷开发 (0)
- DB2 (2)
- 持续集成 (1)
- JVM性能调优 (2)
- Weblogic (0)
- PHP (1)
- Websphere (2)
最新评论
-
di1984HIT:
写的很好,谢谢。。
【转载】【权限控制】角色访问动态生成用户权限菜单树 -
paladin1988:
wangxuehui 写道我昨天也参加视频面试,视频面试2个人 ...
阿里巴巴面试感想 -
wangxuehui:
我昨天也参加视频面试,视频面试2个人,最后都听我说完啦,最后我 ...
阿里巴巴面试感想 -
paladin1988:
liwei113114 写道请问一下,你们最后是怎么解决tld ...
OSGi动态拔插,动态部署,动态权限菜单 -
liwei113114:
请问一下,你们最后是怎么解决tld那个问题的?
我现在也遇到这 ...
OSGi动态拔插,动态部署,动态权限菜单
文章来源:http://www.oecp.cn/hi/yongtree/blog/2099
作者: 谭明智
异常是面向对象语言非常重要的一个特性,良好的异常设计对程序的可扩展性、可维护性、健壮性都起到至关重要。
JAVA根据用处的不同,定义两类异常
* Checked Exception:
Exception的子类,方法签名上需要显示的声明throws,编译器迫使调用者处理这类异常或者声明throws继续往上抛。
* Unchecked Exception:
RuntimeException的子类,方法签名不需要声明throws,编译器也不会强制调用者处理该类异常。
异常的作用和好处:
1. 分离错误代码和正常代码,代码更简洁。
2. 保护数据的正确性和完整性,程序更严谨。
3. 便于调试和排错,软件更好维护。
……
相信很多JAVA开发人员都看到或听到过“不要使用异常来控制流程”,虽然这句话非常易于记忆,但是它并未给出“流程”的定义,所以很难理解作者的本意,让人迷惑不解。
如果“流程”是包括程序的每一步执行,那异常就是用来控制流程的,它就是用来区分程序的正常流程和错误流程,为了更能明确的表达意思,上面这句话应改成
“不要用异常来控制程序的正常流程”。现在带来一个新的问题:如何区分程序正常流程和异常流程?我实在想不出一个评判标准,就举例来说明,大家思维扩散
下。
为了后面更方便的表达,我把异常分成两类,不妥之处请谅解
* 系统异常:
软件的缺陷,客户端对此类异常是无能为力的,通常都是Unchecked Exception。
* 业务异常:
用户未按正常流程操作导致的异常,都是Checked Exception
一个金币转账的例子:需求规定金币一次的转账范围是1~500,如果超过这个额度,就要提示用户金额超出单笔转账的限制。
现在有以下几种场景:
1. 转账的金额是由用户在页面随意输入的:
因为值是用户随意输入的,所以给的值超出限定的范围肯定是司空见惯。我们当然不能把它(输入的值超出限定的范围)归结于异常流程,它应该属于正常流程。
正确的实现如下:
提供一个判断转账金币数量是否超出限定范围的方法
private static final int MAX_PRE_TRANSFER_COIN = 500; public boolean isCoinExceedTransferLimits(int coin) { return coin > MAX_PRE_TRANSFER_COIN; }
Action里先对值进行判断,若不合法,直接返回并提示用户
2. 转账的额度是页面单选框(100、200、300、400、500)选择的:
转账的额度用户不能轻易的更改,但是不排除有些无聊的人会借助其他工具(如,firebug)来修改。此类事件还是占少数的,我们就可以把它归结为非软件bug的异常事件—业务异常(Checked Exception)。
正确的实现如下
CoinExceedTransferLimitExcetion.java
//金币超出限定范围的异常类 public class CoinExceedTransferLimitExcetion extends Exception { private static final long serialVersionUID = -7867713004171563795L; private int coin; public CoinExceedTransferLimitExcetion() { } public CoinExceedTransferLimitExcetion(int coin) { this.coin = coin; } public int getCoin() { return coin; } @Override public String getMessage() { return coin + " is exceed transfer limit:500"; } }
//转账方法
private static final int MAX_PRE_TRANSFER_COIN = 500; public void transferCoin(int coin) throws CoinExceedTransferLimitExcetion { if (coin > MAX_PRE_TRANSFER_COIN) throw new CoinExceedTransferLimitExcetion(coin); // do transfering coin }
3. 接口transferCoin(int coin)的规范里已经定了契约 ,调用transferCoin之前必须要先调用isCoinExceedTransferLimits判断值是否合法:
虽然规范人人都要遵循,但毕竟只是规范,编译器无法强制约束。此时就需要用系统异常(Unchecked Exception)来保证程序的正确性,没遵守规范的就当做软件bug处理。
正确的实现如下:
public class CoinExceedTransferLimitExcetion extends RuntimeException { private static final long serialVersionUID = -7867713004171563795L; public CoinExceedTransferLimitExcetion() { } public CoinExceedTransferLimitExcetion(int coin) { super(coin + " is exceed transfer limit:500"); } }
//转账方法
public void transferCoin(int coin){ if (coin > MAX_PRE_TRANSFER_COIN) throw new CoinExceedTransferLimitExcetion(coin); // do transfering coin }
Action对业务异常的处理:操作具体的异常类
public String execute() { try { userService.transferCoin(coin); } catch (CoinExceedTransferLimitExcetion e) { e.getCoin(); } return SUCCESS; }
public String execute() { try { userService.transferCoin(coin); } catch (RuntimeException e) { LOG.error(e.getMessage()); } return SUCCESS; }
系统异常类恰恰相反,捕获者只会调用getMessage()获取异常信息,然后记录错误日志,所以系统异常类(Uncheck Exception)的实现类对getMessage()方法返回内容比较讲究。
不管是业务异常还是系统异常,都需要提供丰富的信息。如:数据库访问异常,需要提供查询sql语句等;HTTP接口调用异常,需要给出访问的URL和参数列表(如果是post请求,参数列表不提供也可以,取决于维护人员或者开发人员拿到参数列表会如何去使用)。
public class BadSqlGrammarException extends InvalidDataAccessResourceUsageException { private String sql; public BadSqlGrammarException(String task, String sql, SQLException ex) { super(task + "; bad SQL grammar [" + sql + "]", ex); this.sql = sql; } public SQLException getSQLException() { return (SQLException) getCause(); } public String getSql() { return this.sql; } }
public class HttpInvokeException extends RuntimeException { private static final long serialVersionUID = -6477873547070785173L; public HttpInvokeException(String url, String message) { super("http interface unavailable [" + url + "];" + message); } }
1.是软件bug还是业务异常,软件bug是Unchecked Exception,否则是Checked Exception
2.如果把该异常抛给用户,用户能否做出补救。如果客户端无能为力,则用Unchecked Exception,否则抛Checked Exception
结合这两点,两类异常就不会混淆使用了。
异常设计的几个原则:
如果方法遭遇了一个无法处理的意外情况,那么抛出一个异常。
避免使用异常来指出可以视为方法的常用功能的情况。
如果发现客户违反了契约(例如,传入非法输入参数),那么抛出非检查型异常。
如果方法无法履型契约,那么抛出检查型异常,也可以抛出非检查型异常。
如果你认为客户程序员需要有意识地采取措施,那么抛出检查型异常。
异常类应该给客户提供丰富的信息,异常类跟其它类一样,允许定义自己的属性和方法。
异常类名和方法遵循JAVA类名规范和方法名规范
跟JAVA其它类一样,不要定义多余的方法和变量,不会使用的变量,就不要定义。我觉得BadSqlGrammarException.getSql() 就是多余的
以下是我工作当中碰到的一些我认为不是很好的写法,我之前也犯过此类的错误
public class ServiceException extends RuntimeException { private static final long serialVersionUID = 8670135969660230761L; public ServiceException(Exception e) { super(e); } public ServiceException(String message) { super(message); } }
1.业务异常不应该是Unchecked Exception。
2.不存在一个具体的异常类名称是“ServiceException”。
解决方法:定义一个抽象的业务异常“ServiceException”
public abstract class ServiceException extends Exception { private static final long serialVersionUID = -8411541817140551506L; }
try { new String(source.getBytes("UTF-8"), "GBK"); } catch (UnsupportedEncodingException e) { e.printStackTrace(); }
1.环境不支持UTF-8或者GBK很显然是一个非常严重的bug,不能置之不理
2.堆栈的方式记录错误信息不合理,若产品环境是不记录标准输出,这个错误信息就会丢失掉。若产品环境是记录标准输出,万一这段程序被while循环的线程调用,有可能引起硬盘容量溢出,最终导致程序的运行不正常,甚至数据的丢失。
解决方法:捕获UnsupportedEncodingException,封装成Unchecked Exception,往上抛,中断程序的执行。
try { new String(source.getBytes("UTF-8"), "GBK"); } catch (UnsupportedEncodingException e) { throw new IllegalStateException("the base runtime environment does not support 'UTF-8' or 'GBK'"); }
public void transferCoin(int outUid, int inUserUid, int coin) throws CoinNotEnoughException { final User outUser = userDao.getUser(outUid); final User inUser = userDao.getUser(inUserUid); outUser.decreaseCoin(coin); inUser.increaseCoin(coin); try { // BEGIN TRANSACTION userDao.save(outUser); userDao.save(inUser); // END TRANSACTION // log transfer operate } catch (Exception e) { throw new ServiceException(e); } }
1. Service并不是只能抛出业务异常,Service也可以抛出其他异常
如IllegalArgumentException、ArrayIndexOutOfBoundsException或者spring框架的DataAccessException
2. 多数情况下,Dao不会抛Checked Exception给Service,假如所有代码都非常规范,Service类里不应该出现try{}catch代码。
解决方法:删除try{}catch代码
public void transferCoin(int outUid, int inUserUid, int coin) throws CoinNotEnoughException { final User outUser = userDao.getUser(outUid); final User inUser = userDao.getUser(inUserUid); outUser.decreaseCoin(coin); inUser.increaseCoin(coin); // BEGIN TRANSACTION userDao.save(outUser); userDao.save(inUser); // END TRANSACTION // log transfer operate }
D. 创建没有意义的异常
public class DuplicateUsernameException extends Exception { }
1. 它除了有一个"意义明确"的名字以外没有任何有用的信息了。不要忘记Exception跟其他的Java类一样,客户端可以调用其中的方法来得到更多的信息。
解决方案:定义上捕获者需要用到的信息
public class DuplicateUsernameException extends Exception { private static final long serialVersionUID = -6113064394525919823L; private String username = null; private String[] availableNames = new String[0]; public DuplicateUsernameException(String username) { this.username = username; } public DuplicateUsernameException(String username, String[] availableNames) { this(username); this.availableNames = availableNames; } public String requestedUsername() { return this.username; } public String[] availableNames() { return this.availableNames; } }
public class CoinNotEnoughException2 extends Exception { private static final long serialVersionUID = 4724424650547006411L; public CoinNotEnoughException2(String message) { super(message); } } public void decreaseCoin(int forTransferCoin) throws CoinNotEnoughException2 { if (this.coin < forTransferCoin) throw new CoinNotEnoughException2("金币数量不够"); this.coin -= forTransferCoin; }
解决方法:
错误提示的文案统一放在一个配置文件里,根据异常类型获取对应的错误提示信息,若需要支持国际化还可以提供多个语言的版本。
F. 方法签名声明了多余的throws
理由:代码不够精简,调用者不得不加上try{}catch代码
解决方案:若方法不可能会抛出该异常,那就删除多余的throws
G. 给每一个异常类都定义一个不会用到ErrorCode
理由:多一个功能就多一个维护成本
解决方法:不要无谓的定义ErrorCode,除非真的需要(如给别人提供接口调用的,这时,最好对异常进行规划和分类。如1xx代表金币相关的异常,2xx代表用户相关的异常…
1.异常设计
http://www.cnblogs.com/JavaVillage/articles/384483.html(翻译)
http://www.javaworld.com/jw-07-1998/jw-07-techniques.html(原文)
2. 异常处理最佳实践
http://tech.e800.com.cn/articles/2009/79/1247105040929_1.html(翻译)
http://onjava.com/pub/a/onjava/2003/11/19/exceptions.html(原文)
相关推荐
* 异常处理的主要步骤是:抛出异常(throw) -> 捕捉异常(catch) -> 处理异常(finally)。 三、异常类的继承关系 * `Throwable` 是所有异常类的父类。 * `Error` 和 `Exception` 是 `Throwable` 的两个子类。 *...
Java 异常处理的设计原则是指在设计时把那些有可能发生异常情况的语句放在 try 块中,通过该语句块来打开 Java 的处理异常机制,然后检查有可能出现错误的程序代码。若异常条件满足,异常将被抛出,原程序流程在此...
在本实验中,你会学习如何在代码中使用`try-catch-finally`结构来处理异常,并结合log4j进行日志记录。这包括设置log4j配置文件,创建Logger对象,以及在不同日志级别下记录信息。理解这些基本操作后,你将能够更好...
4. **不要忽视异常**:即使是在测试环境中,也应该妥善处理异常,避免因为忽视异常而导致的问题积累。 综上所述,Java异常处理机制为开发者提供了一套完整的解决方案,帮助他们在面对程序中的各种异常情况时能够...
3. **分层次处理异常**:在设计异常处理时,应遵循从具体到抽象的顺序,即先捕获特定的子类异常,然后才是更一般的父类异常。这样可以提供更精确的异常信息,帮助调用者更好地理解异常原因。 4. **捕获特定异常**:...
- 捕获(Catching):当异常发生时,程序会尝试通过`try-catch`块来捕获并处理异常。 - 抛出(Throwing):当遇到无法正常处理的情况时,可以使用`throw`关键字抛出一个异常。 - 异常处理链(Exception Chaining...
java异常练习题,北大青鸟学士后课后练习,主要是异常处理的小测验
在这个过程中,Java使用了try-catch语句块来捕获和处理异常。通过在一个或多个catch语句块中处理所有可能抛出的异常,可以实现全局异常统一处理。 例如,在Spring框架中,使用@ControllerAdvice和@ExceptionHandler...
如果一个方法没有处理异常,但知道可能会抛出异常,可以使用`throws`声明。例如: ```java public int divide(int a, int b) throws ArithmeticException { return a / b; } ``` 5. `throw`关键字和自定义异常 `...
`try`块包含可能抛出异常的代码,`catch`块用于捕获并处理异常,`finally`块包含在任何情况下都需要执行的代码,无论是否发生异常。`throw`关键字用于显式抛出一个异常,而`throws`则用于声明方法可能会抛出的异常。...
try-catch语句是Java语言中异常处理的基本语句,用于捕获和处理异常。自定义异常类是指用户定义的异常类,用于处理特定的异常情况。 四、实验组织运行要求 实验组织运行要求如下: * 集中授课:10% * 学生自主...
`try`块用于包含可能会抛出异常的代码,`catch`块用于捕获并处理异常,`finally`块确保在任何情况下都会执行的代码,无论是否发生异常。`throw`用于手动抛出一个异常,而`throws`则用于声明方法可能抛出的异常。 1....
在 Java 中,有两种方式来处理异常:try-catch 语句和 throws 语句。 * try-catch 语句:用于捕获和处理异常。try 语句块中包含可能出现异常的代码,catch 语句块中包含异常处理代码。 * throws 语句:用于声明可能...
14.java异常处理机制.zip14.java异常处理机制.zip14.java异常处理机制.zip14.java异常处理机制.zip14.java异常处理机制.zip14.java异常处理机制.zip14.java异常处理机制.zip14.java异常处理机制.zip14.java异常处理...
Java 异常处理机制包括抛出异常、捕获异常和处理异常等。 抛出异常是指在方法中throws语句抛出异常,例如throws IOException。捕获异常是指使用try-catch语句捕获异常,例如try { ... } catch (IOException e) { .....
Java中处理异常主要依赖于`try`、`catch`和`finally`这三个关键字。`try`块用于包含可能会抛出异常的代码,`catch`块用来捕获并处理这些异常,而`finally`块则确保无论是否发生异常,都会执行的代码,例如关闭文件流...
通过上面的知识点,可以看出异常处理在Java程序设计中的重要性,良好的异常处理可以提高程序的健壮性和用户体验。此外,合理地使用异常类的层次结构和自定义异常可以帮助开发者创建更为清晰、可维护的代码。
Java自定义异常处理详细介绍,包括了很多比较经典的案例,可以自己研究自定义异常
处理编译时异常通常有两种方式:使用try-catch语句块捕获并处理异常,或者使用throws关键字声明异常,将异常处理的责任交给调用该方法的代码。 接下来,我们讨论运行时异常(Runtime Exception)。运行时异常是程序...
Java 通过面向对象的方法来处理异常。对异常的处理有两种方式:捕获处理异常和抛出异常。捕获和处理异常可以使用 try...catch...finally 结构来实现,语句格式为: ``` try { 可能出现异常的语句 } catch (异常类名...