`
luckaway
  • 浏览: 136793 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

JAVA异常设计原则

阅读更多
异常是面向对象语言非常重要的一个特性,良好的异常设计对程序的可扩展性、可维护性、健壮性都起到至关重要。
JAVA根据用处的不同,定义了两类异常
    * Checked Exception: Exception的子类,方法签名上需要显示的声明throws,编译器迫使调用者处理这类异常或者声明throws继续往上抛。
    * Unchecked Exception: RuntimeException的子类,方法签名不需要声明throws,编译器也不会强制调用者处理该类异常。

异常的作用和好处:
1. 分离错误代码和正常代码,代码更简洁。
2. 保护数据的正确性和完整性,程序更严谨。
3. 便于调试和排错,软件更好维护。
……

相信很多JAVA开发人员都看到或听到过“不要使用异常来控制流程”,虽然这句话非常易于记忆,但是它并未给出“流程”的定义,所以很难理解作者的本意,让人迷惑不解。

如果“流程”是包括程序的每一步执行,我认为异常就是用来控制流程的,它就是用来区分程序的正常流程和错误流程,为了更能明确的表达意思,上面这句话应改成“不要用异常来控制程序的正常流程”。现在带来一个新的问题就是如何区分程序正常流程和异常流程?我实在想不出一个评判标准,就举例来说明,大家思维扩散下。

为了后面更方便的表达,我把异常分成两类,不妥之处请谅解

    * 系统异常:软件的缺陷,客户端对此类异常是无能为力的,通常都是Unchecked Exception。
    *业务异常:用户未按正常流程操作导致的异常,都是Checked Exception

金币转帐例子
1. 需求规定金币一次的转账范围是1~500,如果超过这个额度,就要提示用户金额超出单笔转账的限制,转账的金额是由用户在页面输入的:
因为值是用户输入的,所以给的值超出限定的范围肯定是司空见惯。我们当然不能把它(输入的值超出限定的范围)归结于异常流程,它应该属于正常流程,应该提供验证数据的完整功能。
正确的实现如下:
提供一个判断转账金币数量是否超出限定范围的方法

private static final int MAX_PRE_TRANSFER_COIN = 500;

public boolean isCoinExceedTransferLimits(int coin) {
return coin > MAX_PRE_TRANSFER_COIN;
}


Action里先对值进行校验,若不合法,直接返回并提示用户

2. 在转账的过程里,发些金币数量不够:
我们的程序都是运行在并发环境中,Action无法完全做到判断金币是否足够。因为在判断之后和事务之前的刹那间,有可能产生其他扣费操作导致金币不够。这时我们就需要用业务异常(Checked Exception)来控制

正确的实现如下
CoinNotEnoughExcetion .java
//金币不够的异常类
public class CoinNotEnoughExcetion extends Exception {
private static final long serialVersionUID = -7867713004171563795L;
private int coin;
public CoinNotEnoughExcetion() {
}

public CoinNotEnoughExcetion(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 CoinNotEnoughExcetion{
if (!hasEnoughCoin())
throw new CoinNotEnoughExcetion(coin);
// do transfering coin
}



3. 接口transferCoin(int coin)的规范里已经定了契约,调用transferCoin之前必须要先调用isCoinExceedTransferLimits判断值是否合法:
虽然规范人人都要遵循,但毕竟只是规范,编译器无法强制约束。此时就需要用系统异常(Unchecked Exception,用JDK的标准异常)来保证程序的正确性,没遵守规范的都当做软件bug处理。
正确的实现如下:

//转账方法
public void transferCoin(int coin){
if (coin > MAX_PRE_TRANSFER_COIN)
throw new IllegalArgumentException(coin+" is exceed tranfer limits:500");
// do transfering coin
}


至此,举例已经结束了,在这里再延伸下—业务异常和系统异常类在实现上的区别,该区别的根源来自于调用者捕获到异常后是如何处理的

Action对业务异常的处理:操作具体的异常类

public String execute() {
try {
userService.transferCoin(coin);
} catch (CoinExceedTransferLimitExcetion e) {
e.getCoin();
}
return SUCCESS;
}


Action对系统异常的处理:无法知道具体的异常类
public String execute() {
try {
userService.transferCoin(coin);
} catch (RuntimeException e) {
LOG.error(e.getMessage());
}
return SUCCESS;
}


调用者捕获业务异常(Checked Excetion)之后,通常不会去调用getMessage()方法的,而是调用异常类里特有的方法,所以业务异常类的实现要注重特有的,跟业务相关的方法,而不是getMessage()方法。
系统异常类恰恰相反,捕获者只会调用getMessage()获取异常信息,然后记录错误日志,所以系统异常类(Uncheck Exception)的实现类对getMessage()方法返回内容比较讲究。

不管是业务异常还是系统异常,都需要提供丰富的信息。如:数据库访问异常,需要提供查询sql语句等;HTTP接口调用异常,需要给出访问的URL和参数列表(如果是post请求,参数列表不提供也可以,取决于维护人员或者开发人员拿到参数列表会如何去使用)。
1. Sql语法错误异常类(取自spring框架的异常类,Spring的异常体系很强大,值得一看):

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;
}
}


2. HTTP接口调用异常类

public class HttpInvokeException extends RuntimeException {
private static final long serialVersionUID = -6477873547070785173L;

public HttpInvokeException(String url, String message) {
super("http interface unavailable [" + url + "];" + message);
}
}


如何选择用Unchecked Exception和Checked Exception
1.是软件bug还是业务异常,软件bug是Unchecked Exception,否则是Checked Exception
2.如果把该异常抛给用户,用户能否做出补救。如果客户端无能为力,则用Unchecked Exception,否则抛Checked Exception
结合这两点,两类异常就不会混淆使用了。


异常设计的几个原则:
1.如果方法遭遇了一个无法处理的意外情况,那么抛出一个异常。
2.避免使用异常来指出可以视为方法的常用功能的情况。
3.如果发现客户违反了契约(例如,传入非法输入参数),那么抛出非检查型异常。
4.如果方法无法履型契约,那么抛出检查型异常,也可以抛出非检查型异常。
5.如果你认为客户程序员需要有意识地采取措施,那么抛出检查型异常。
6.异常类应该给客户提供丰富的信息,异常类跟其它类一样,允许定义自己的属性和方法。
7.异常类名和方法遵循JAVA类名规范和方法名规范
8.跟JAVA其它类一样,不要定义多余的方法和变量。(不会使用的变量,就不要定义,spring的BadSqlGrammarException.getSql() 就是多余的)


以下是我工作当中碰到的一些我认为不是很好的写法,我之前也犯过此类的错误
A. 整个业务层只定义了一个异常类
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;
}


B. 忽略异常
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'");
}


C. 捕获顶层的异常—Exception
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;
}
}

E. 把展示给用户的信息直接放在异常信息里。
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(原文)
分享到:
评论
36 楼 Dawn.yang 2011-03-31  
我个人觉得,checked exception 更多的是用来控制流程,至于楼主说的“正确的流程”,没有明确的定义,也很难定义
35 楼 luckaway 2011-01-06  
引用
    以前有部分项目是这么处理的,在Action里面。那时候项目并没有分层,直接在Action里调用封装好了hibernate的CURD方法。开发很快速,但是代码也比较乱。



try{
//code...
if(不满足某条件)
   throw new XXExcption("请填写必要的值");
//other code...
if(不满足某条件)
   throw new XXException("你操作的是非法的");
//other code...
//执行成功,跳转到成功页面或者是通过json返回ajax的请求。
}catch(Exception e){
//出错了,那就跳转到错误页面或者通过json封装错误(e.getMessage)返回ajax请求。
}



   后来直接就抛出RuntimeException或者自己实现的继承RuntimeException的异常类,然后不管是通过Filter还是Struts2的Interceptor去统一截获异常并处理。


   看了很多关于异常的文章,现在依然迷糊。


Action不应该再抛异常了 

try{
//code...
if(不满足某条件){
   context.put("errorMessage","请填写必要的值");
   return INPUT;
}
//other code...
if(不满足某条件){
   context.put("errorMessage","你操作的是非法的");
   return INPUT;
}
//other code...
//执行成功,跳转到成功页面或者是通过json返回ajax的请求。
}catch(RuntimeException e){
   context.put("errorMessage","服务器出现错误,请联系管理员");
   return ERROR;
}


}catch(RuntimeException e){
   context.put("errorMessage","服务器出现错误,请联系管理员");
   return ERROR;
}
或者
}catch(Exception e){
   context.put("errorMessage","服务器出现错误,请联系管理员");
   return ERROR;
}
不一定在每个Action里都处理,可以统一处理。
34 楼 accphc 2011-01-06  
<p><br>    以前有部分项目是这么处理的,在Action里面。那时候项目并没有分层,直接在Action里调用封装好了hibernate的CURD方法。开发很快速,但是代码也比较乱。</p>
<p> </p>
<p> </p>
<pre name="code" class="java">try{
//code...
if(不满足某条件)
   throw new XXExcption("请填写必要的值");
//other code...
if(不满足某条件)
   throw new XXException("你操作的是非法的");
//other code...
//执行成功,跳转到成功页面或者是通过json返回ajax的请求。
}catch(Exception e){
//出错了,那就跳转到错误页面或者通过json封装错误(e.getMessage)返回ajax请求。
}
</pre>
 
<p>   后来直接就抛出RuntimeException或者自己实现的继承RuntimeException的异常类,然后不管是通过Filter还是Struts2的<span style="font-family: arial; font-size: small; line-height: 18px;">Interceptor去统一截获异常并处理。</span></p>
<p> </p>
<p>   看了很多关于异常的文章,现在依然迷糊。</p>
33 楼 luckaway 2011-01-05  
gdpglc 写道
luckaway 写道
gdpglc 写道

dao 可以不抛checked exception. 但是,出现下面的情况,是不是需要在service里边处理异常呢?

比如某个Service方法写成这样了呢:

1. file logic
2. dao invoking logic



不是很明白你的意思,file logic不应该直接在Service处理,操作File都应该放在Dao里处理


为什么呀? 那操作email、jms等等呢,也都放到Dao里吗?
你在Dao处理文件IO异常的方式,好象是说,如果有IO问题,就算程序Bug是这个意思吗?因为你抛的是一个unchecked exception.

我没用过Spring,不好意思,可能问的很初级。


如果email抛出的是业务异常,用户是能为此做纠正的,比如邮件地址不存在,那就要继续往上抛,若觉之前的业务异常不过明确或者提供的信息不够,比如邮件地址不存在,但是它并未提供那个邮件地址不存在,你也可以封装成另外一个带邮件地址的明确的业务异常。

如果是用户是无能为力的,比如IOException,那就转换成RuntimeException。直接在Service里做转换也是可以的
32 楼 luckaway 2011-01-05  
yanical 写道

就个例来说,首先这种情况不应该抛出异常,不管什么异常。而是要提供数据验证功能。服务端不该依赖客户端来保证数据完整性吧? 如果换个客户端实现不完了。

异常是不是就只该用在没法预料的情况呢? 我觉得可以斟酌一下,像这个例子肯定是可以预料的情况,就应该是验证的功能。

到目前为止我觉得有必要用Checked Exception的地方也只有IO的时候的异常


言之有理,这个是应该属于简单的数据验证功能,不应该有异常来控制,这个例子是不是合适,误导大家了!
我等会来改下
31 楼 yanical 2011-01-05  
luckaway 写道
yanical 写道
luckaway 写道

2. 转账的额度是页面单选框(100、200、300、400、500)选择的:
转账的额度用户不能轻易的更改,但是不排除有些无聊的人会借助其他工具(如,firebug)来修改。此类事件还是占少数的,我们就可以把它归结为非软件bug的异常事件—业务异常(Checked Exception)。
正确的实现如下

不错。不过比较疑惑的还是什么时候用Checked Exception,什么时候用Un-Checked Exception。
一直赞同的一个观点是:只有在客户端可以通过某些手段来弥补发生的错误时才使用Checked Exception。
似乎楼主也支持这个观点?但是我一直没有发现过那个地方可以确定客户端可以弥补错误,包括楼主举的上面这个例子,我觉得这个时候客户端也没法做什么了。而且为了这种小可能做成Checked Exception,让调用的地方都要来处理,好像把代码搞混乱了。

改成Unchecked Exception我们只能提示用户“系统错误”之类的,感觉就是软件bug了。

我个人觉得这种事件,还是可以容忍的,应该给用户一个明确的提示,告诉它不要修改radio值


就个例来说,首先这种情况不应该抛出异常,不管什么异常。而是要提供数据验证功能。服务端不该依赖客户端来保证数据完整性吧? 如果换个客户端实现不完了。

异常是不是就只该用在没法预料的情况呢? 我觉得可以斟酌一下,像这个例子肯定是可以预料的情况,就应该是验证的功能。

到目前为止我觉得有必要用Checked Exception的地方也只有IO的时候的异常
30 楼 gdpglc 2011-01-05  
luckaway 写道
gdpglc 写道

dao 可以不抛checked exception. 但是,出现下面的情况,是不是需要在service里边处理异常呢?

比如某个Service方法写成这样了呢:

1. file logic
2. dao invoking logic



不是很明白你的意思,file logic不应该直接在Service处理,操作File都应该放在Dao里处理


为什么呀? 那操作email、jms等等呢,也都放到Dao里吗?
你在Dao处理文件IO异常的方式,好象是说,如果有IO问题,就算程序Bug是这个意思吗?因为你抛的是一个unchecked exception.

我没用过Spring,不好意思,可能问的很初级。
29 楼 luckaway 2011-01-05  
gdpglc 写道

dao 可以不抛checked exception. 但是,出现下面的情况,是不是需要在service里边处理异常呢?

比如某个Service方法写成这样了呢:

1. file logic
2. dao invoking logic



不是很明白你的意思,file logic不应该直接在Service处理,操作File都应该放在Dao里处理
28 楼 gdpglc 2011-01-05  
luckaway 写道
gdpglc 写道
luckaway 写道
gdpglc 写道
C. 捕获顶层的异常—Exception

没明白你的意思,你是说不能用Exception 一次性catch代码里抛出的异常吗?

是的,IllegalArgumentException或者NullPointException等RuntimeException就要保持原味的,但是Action要捕获所有的RuntimeException异常!

我理解,你的意思是,没必要catch这些运行时异常吧?
如果Dao有若干个checked exception,是不是用Exception catch就可以了?
更具体点,我的意思是,比如先做一个文件操作先后调Dao,这时总体上我用一个Exception作catch有问题吗?


Dao的Checked Exception的不应该抛到Service,这些异常跟业务是没关系的,比如是文件访问出错,在Dao里做如下处理。
	public void readFile(String fileName) {
		File file = new File(fileName);
		try {
			FileInputStream fileInputStream = new FileInputStream(file);
		} catch (IOException e) {
			throw new FileResourceAccessException("IOException while operate " + fileName, e);
		}
	}



有时候我们可能用异常来实现某个业务需求,比如需求测试某条sql是否可用,而现有的API没直接提供检测的方法。
那实现方法如下:

XXXDao.java

public boolean isSqlValid(String sql){
   try{
      //do sql query          
      return true;
    }catch(SQlException e){
     return false;
    }
}


至少我还没想到那种情景下Dao的Checked Exception需要抛到Service层。





dao 可以不抛checked exception. 但是,出现下面的情况,是不是需要在service里边处理异常呢?

比如某个Service方法写成这样了呢:

1. file logic
2. dao invoking logic

27 楼 luckaway 2011-01-05  
gdpglc 写道
luckaway 写道
gdpglc 写道
C. 捕获顶层的异常—Exception

没明白你的意思,你是说不能用Exception 一次性catch代码里抛出的异常吗?

是的,IllegalArgumentException或者NullPointException等RuntimeException就要保持原味的,但是Action要捕获所有的RuntimeException异常!

我理解,你的意思是,没必要catch这些运行时异常吧?
如果Dao有若干个checked exception,是不是用Exception catch就可以了?
更具体点,我的意思是,比如先做一个文件操作先后调Dao,这时总体上我用一个Exception作catch有问题吗?


Dao的Checked Exception的不应该抛到Service,这些异常跟业务是没关系的,比如是文件访问出错,在Dao里做如下处理。
	public void readFile(String fileName) {
		File file = new File(fileName);
		try {
			FileInputStream fileInputStream = new FileInputStream(file);
		} catch (IOException e) {
			throw new FileResourceAccessException("IOException while operate " + fileName, e);
		}
	}



有时候我们可能用异常来实现某个业务需求,比如需求测试某条sql是否可用,而现有的API没直接提供检测的方法。
那实现方法如下:

XXXDao.java

public boolean isSqlValid(String sql){
   try{
      //do sql query          
      return true;
    }catch(SQlException e){
     return false;
    }
}


至少我还没想到那种情景下Dao的Checked Exception需要抛到Service层。



26 楼 gdpglc 2011-01-05  
luckaway 写道
gdpglc 写道
C. 捕获顶层的异常—Exception

没明白你的意思,你是说不能用Exception 一次性catch代码里抛出的异常吗?

是的,IllegalArgumentException或者NullPointException等RuntimeException就要保持原味的,但是Action要捕获所有的RuntimeException异常!

我理解,你的意思是,没必要catch这些运行时异常吧?
如果Dao有若干个checked exception,是不是用Exception catch就可以了?
更具体点,我的意思是,比如先做一个文件操作然后调Dao,这时总体上我用一个Exception作catch有问题吗?
25 楼 luckaway 2011-01-05  
gdpglc 写道
C. 捕获顶层的异常—Exception

没明白你的意思,你是说不能用Exception 一次性catch代码里抛出的异常吗?

是的,IllegalArgumentException或者NullPointException等RuntimeException就要保持原味的,但是Action要捕获所有的RuntimeException异常!
24 楼 tangay 2011-01-05  
呵呵,我感觉是这样的,其实出现异常的时候,一般来说有2种视角,一种是开发者的视角,一种是用户的视角。对于开发者来说,出现异常时,要求记录log,异常信息越详细越好。对于用户来说,他们是不关心内部细节,所以只要给他们容易理解的语言提示就好。
所以一般来说,出现底层模块出现异常时,我们会先在异常现场用log4j等工具记录异常的堆栈信息,然后包装之重新向上层模块抛出,上层一般会在一个比较适合的层次集中处理底层抛出的异常,并将易理解的错误信息报告给用户。


至于unchecked 和 checked 异常,我感觉其实是这样的:

checked异常使用场景:就是我一个操作的结果并不是确定的,一般是可以成功的,但是也有一定几率失败,而且这个失败不是你这层可控的,比如说读写文件,网络io,访问数据库,这个时候,你就要声明checked 异常了。也就是提示你的接口的使用者注意,你调用我是有不可预知的错误情景,你一定要主动防备。

unchecked异常是运行时异常,就是说这个异常出现是跟你当前执行模块的上下文相关的,其实多半是程序bug,或者参数超出临界值等原因造成的,出现这种情况是谁都没折的,你也是无法防备的。
23 楼 gdpglc 2011-01-05  
C. 捕获顶层的异常—Exception

没明白你的意思,你是说不能用Exception 一次性catch代码里抛出的异常吗?
22 楼 yjfkk 2011-01-05  
引用

都需要提供丰富的信息。如:数据库访问异常,需要提供查询sql语句等;HTTP接口调用异常,需要给出访问的URL和参数列表(如果是post请求,参数列表不提供也可以,取决于维护人员或者开发人员拿到参数列表会如何去使用)。


写的不错,提供信息非常重要,对查找错误的原因有很多的帮助。
大部分开发人员,只会创建没有意义的异常类,除了有个性名字外,就没有其他额外方法了。

设计异常类要跟普通类要一样,要有自己的属性和方法

21 楼 luckaway 2011-01-05  
badqiu 写道
luckaway 写道
badqiu 写道

没搞明白你,难道你 Checked Exception带的信息就比我 UncheckedException信息丰富? 我是说异常体系根本就不需要Checked Exception存在,然后你所要完成的功能全部由 UncheckedException完成。Checked,Unchecked只有在调用处强制处理异常的区别,功能完全是一样的。

举个例子。

我们的servlet要调用你的一个 service方法.
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException,IOException {
    // 现在我只能抛出 ServletException,IOException,抛不出去SomeCheckedException
    try {
        someService.execute();
    }catch(SomeCheckedException e) {
        // 你竟然要求我立即在这里 处理异常,连给我通过框架统一处理异常的机会都没有,不SB么?
        // 所以,因为CheckdException,我在这里只能 log一次,或者是 throw new RuntimeException(e);
    }
}



如果仅仅是记录错误日志或者throw new RuntimeException(e),是没必用Checked Exception。JDK的Checked Exception,如:SqlException、UnsupportedEncodingException ,我们都是简单的封装成runtime Exception,也不会增加额外的信息,这种场景下,确实不应该是Checked Exception。


Unchecked Exception和Checked Exception都能携带信息,Unchecked Exception里携带的信息只会给日志记录用。
Checked Exception携带信息更多的作用是给用户看的,甚至有可能都不记录日志了。

public String execute() {
	try {
		userService.transferCoin(coin);
	} catch (CoinNotEnoughException e) {
		context.put("errorMessage", "你目前的金币是" + e.getTotalCoin() + ",无法转账" + e.getForTransferCoin());
	}
	return SUCCESS;
}



如你自己的demo,你的调用方一定需要强制处理该异常了。 已经没有办法使用框架来处理异常。

你的Checked Exception只是你自己一厢情愿的认为按你现在的方式处理。
难道其它语言没有 Checked Exception就不用活了。 硬按你这种规则区分。

SqlException,UnsupportedEncodingException, DateFormat ParseException 都是JDK败笔,已经懒得说了。


我一直所说使用Checked Exception最主要的缺点: 接口污染,强制调用者立即处理。
所以不用跟我说 Exception转换为message的程序了. 因为要转换为消息的时候,我也直接处理CoinNotEnoughException 这个RuntimeException. 而不是Checked Exception


是有弊端的,异常太多,代码就会比较丑陋! 强制处理,程序会更健壮点,如果是RuntimeExcetion可能会漏掉。

我一般定义接口的时候,都先跟同事先交流,是如何控制。 避免定义了Checked Exception,而不使用的局面。

20 楼 william_ai 2011-01-04  
计算机软件异常

  Exception 机制
  为什么要自定义自己的Exception ,Java Exception机制与传统的C语言的异常处理机制有什么不同,这种Exception机制的意义在什么地方?接下来咱就来和你一起探讨Exception 的优缺点。
  早期的C语言的异常处理机制,通常是我们人为的对返回结果加一些标志来进行判定,比如发生错误返回什么标志,正常情况下我们又是返回什么标记,而这些都不是语言本身所赋予我们的,而对于C语言这种机制又有什么问题哩?为什么新一代的语言 Java Ruby C# 等都用Exception机制而不是维持C语言的老样子?这些都是我们需要思考的问题。
  C语言的异常处理机制全是我们人为的定义,这样就会造成业务逻辑的主线受到异常处理的牵制,或者说是我们难免会将注意力转移,并且造成业务逻辑与异常处理之间有很大程度上的缠绕。
  理论上异常处理划分为两个模型(中止模型与继续模型),但实际使用方面我们对中止模型用的比较多,这个模型比较实用,而继续模型则不是那么的应用普遍,多少是耦合的过于紧密。
  中止模型 :
  假设错误非常严重,已至你无法在回到错误发生的地方,也就是说,这段程序经过判断认为,他已经没有办法挽回,于是就抛出异常,希望这个异常不要在回来,这也是Java 当前所采用的模式。
  继续模型:
  这种模型的主旨是恢复当前的运行环境,然后希望能够重新回到错误的发生地,并希望第二次的尝试能够获得成功,这种模型通常为操作系统所应用。
  异常的常见例子有内存分配不足、数组下标越界、运算溢出或除数为零。
  使用异常的原因(参考csdn):取代返回值表示错误状态、使程序可以继续运行、发生异常时记录错误信息……
  C++异常处理析构类对象、已调用的函数退栈……
  Java中的异常
  异常:程序在运行过程中发生由于硬件设备问 题、软件设计错误等导致的程序异常事件
  异常本身是一个对象,产生异常就是产生了一个异常对象。
  通过try…catch语句进行捕获异常。
  异常都是从类Throwable派生出来。

出处:http://baike.baidu.com/view/209658.htm
19 楼 davepkxxx 2011-01-04  
joknm 写道
yanical 写道
不要用异常来控制程序的正常流程


LZ一语道破。
最近也很纠结这个问题。

我一般是用来控制业务流程
18 楼 badqiu 2011-01-04  
luckaway 写道
badqiu 写道

没搞明白你,难道你 Checked Exception带的信息就比我 UncheckedException信息丰富? 我是说异常体系根本就不需要Checked Exception存在,然后你所要完成的功能全部由 UncheckedException完成。Checked,Unchecked只有在调用处强制处理异常的区别,功能完全是一样的。

举个例子。

我们的servlet要调用你的一个 service方法.
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException,IOException {
    // 现在我只能抛出 ServletException,IOException,抛不出去SomeCheckedException
    try {
        someService.execute();
    }catch(SomeCheckedException e) {
        // 你竟然要求我立即在这里 处理异常,连给我通过框架统一处理异常的机会都没有,不SB么?
        // 所以,因为CheckdException,我在这里只能 log一次,或者是 throw new RuntimeException(e);
    }
}



如果仅仅是记录错误日志或者throw new RuntimeException(e),是没必用Checked Exception。JDK的Checked Exception,如:SqlException、UnsupportedEncodingException ,我们都是简单的封装成runtime Exception,也不会增加额外的信息,这种场景下,确实不应该是Checked Exception。


Unchecked Exception和Checked Exception都能携带信息,Unchecked Exception里携带的信息只会给日志记录用。
Checked Exception携带信息更多的作用是给用户看的,甚至有可能都不记录日志了。

public String execute() {
	try {
		userService.transferCoin(coin);
	} catch (CoinNotEnoughException e) {
		context.put("errorMessage", "你目前的金币是" + e.getTotalCoin() + ",无法转账" + e.getForTransferCoin());
	}
	return SUCCESS;
}



如你自己的demo,你的调用方一定需要强制处理该异常了。 已经没有办法使用框架来处理异常。

你的Checked Exception只是你自己一厢情愿的认为按你现在的方式处理。
难道其它语言没有 Checked Exception就不用活了。 硬按你这种规则区分。

SqlException,UnsupportedEncodingException, DateFormat ParseException 都是JDK败笔,已经懒得说了。


我一直所说使用Checked Exception最主要的缺点: 接口污染,强制调用者立即处理。
所以不用跟我说 Exception转换为message的程序了. 因为要转换为消息的时候,我也直接处理CoinNotEnoughException 这个RuntimeException. 而不是Checked Exception
17 楼 luckaway 2011-01-04  
badqiu 写道

没搞明白你,难道你 Checked Exception带的信息就比我 UncheckedException信息丰富? 我是说异常体系根本就不需要Checked Exception存在,然后你所要完成的功能全部由 UncheckedException完成。Checked,Unchecked只有在调用处强制处理异常的区别,功能完全是一样的。

举个例子。

我们的servlet要调用你的一个 service方法.
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException,IOException {
    // 现在我只能抛出 ServletException,IOException,抛不出去SomeCheckedException
    try {
        someService.execute();
    }catch(SomeCheckedException e) {
        // 你竟然要求我立即在这里 处理异常,连给我通过框架统一处理异常的机会都没有,不SB么?
        // 所以,因为CheckdException,我在这里只能 log一次,或者是 throw new RuntimeException(e);
    }
}



如果仅仅是记录错误日志或者throw new RuntimeException(e),是没必用Checked Exception。JDK的Checked Exception,如:SqlException、UnsupportedEncodingException ,我们都是简单的封装成runtime Exception,也不会增加额外的信息,这种场景下,确实不应该是Checked Exception。


Unchecked Exception和Checked Exception都能携带信息,Unchecked Exception里携带的信息只会给日志记录用。
Checked Exception携带信息更多的作用是给用户看的,甚至有可能都不记录日志了。

public String execute() {
	try {
		userService.transferCoin(coin);
	} catch (CoinNotEnoughException e) {
		context.put("errorMessage", "你目前的金币是" + e.getTotalCoin() + ",无法转账" + e.getForTransferCoin());
	}
	return SUCCESS;
}

相关推荐

    Java异常处理的设计原则.pdf

    Java异常处理的设计原则

    深入探索高效的Java异常处理框架

    本文从Java异常最基本的概念、语法开始讲述了Java异常处理的基本知识,分析了Java异常体系结构,对比Spring的异常处理框架,阐述了异常处理的基本原则。并且作者提出了自己处理一个大型应用系统异常的思想,并通过...

    JAVA程序设计教程

    第一章程序和程序设计 .......................................................................................................1 §1.1 什么是程序 ?........................................................

    java课程设计指导书.doc

    其主要目的是使学生深入 理解并实践在本课程中 ,所学的有关JAVA 程序设计方面的语言基础、异常处理、事件处理、小应用程序以及图形用户界设计等。 本课程设计是为了配合Java 程序设计课程,帮助学生深入学习掌握...

    《Java程序设计案例教程》课程标准.doc

    课程 设置坚持以能力为本位的设计原则,倡导以学生为主体的教育理念,把提高学生的技术 应用能力放在重要位置,形成网络工程设计与施工、网络管理与维护、网站建设与管理 、信息类网络基础服务和网络信息安全建设五...

    JAVA 2平台安全技术-结构,API设计和实现

    7.1 设计原则 117 7.2 密码服务和服务提供器 118 7.3 密码类 121 7.3.1 java.security.Security 121 7.3.2 java.security.Provider 122 7.3.3 java.security.MessageDigest 122 7.3.4 java.security.Signature 123 ...

    Java 2平台安全技术-结构,API设计和实现

    7.1 设计原则 117 7.2 密码服务和服务提供器 118 7.3 密码类 121 7.3.1 java.security.Security 121 7.3.2 java.security.Provider 122 7.3.3 java.security.MessageDigest 122 7.3.4 java.security.Signature 123 ...

    JAVA上百实例源码以及开源项目

    例如,容易实现协议的设计。 Java EJB中有、无状态SessionBean的两个例子 两个例子,无状态SessionBean可会话Bean必须实现SessionBean,获取系统属性,初始化JNDI,取得Home对象的引用,创建EJB对象,计算利息等;...

    JAVA上百实例源码以及开源项目源代码

    例如,容易实现协议的设计。 Java EJB中有、无状态SessionBean的两个例子 两个例子,无状态SessionBean可会话Bean必须实现SessionBean,获取系统属性,初始化JNDI,取得Home对象的引用,创建EJB对象,计算利息等;...

    【白雪红叶】JAVA学习技术栈梳理思维导图.xmind

    设计原则 单一职责原则 开闭原则 里氏替换原则 依赖倒转原则 接口隔离原则 迪米特原则 设计模式 结构模式 适配器模式 桥接模式 组合模式 装饰模式 外观模式 享元模式 代理模式 创建模式 抽象工厂...

    Java高级程序设计实战教程第一章-Java编码规范.pptx

    Java高级程序设计 第1章 Java编码规范 1.1 应用场景1.2 相关知识1.3 任务实施1.4 拓展知识1.5 拓展训练1.6课后小结 Java高级程序设计实战教程第一章-Java编码规范全文共13页,当前为第1页。 1.1 应用场景 一个大型的...

    java开源包11

    设计原则 容易维护扩展(不需要修改主类就可以添加新的API支持) 注入型解释器(依据不同的返回格式注入相应的解释器) 集中管理请求参数与参数映射 以运行时异常的方式来管理错误的响应 使用泛型来做强类型编程 多...

    java开源包6

    设计原则 容易维护扩展(不需要修改主类就可以添加新的API支持) 注入型解释器(依据不同的返回格式注入相应的解释器) 集中管理请求参数与参数映射 以运行时异常的方式来管理错误的响应 使用泛型来做强类型编程 多...

    java开源包9

    设计原则 容易维护扩展(不需要修改主类就可以添加新的API支持) 注入型解释器(依据不同的返回格式注入相应的解释器) 集中管理请求参数与参数映射 以运行时异常的方式来管理错误的响应 使用泛型来做强类型编程 多...

    java开源包101

    设计原则 容易维护扩展(不需要修改主类就可以添加新的API支持) 注入型解释器(依据不同的返回格式注入相应的解释器) 集中管理请求参数与参数映射 以运行时异常的方式来管理错误的响应 使用泛型来做强类型编程 多...

    java开源包4

    设计原则 容易维护扩展(不需要修改主类就可以添加新的API支持) 注入型解释器(依据不同的返回格式注入相应的解释器) 集中管理请求参数与参数映射 以运行时异常的方式来管理错误的响应 使用泛型来做强类型编程 多...

    Java面试宝典和毕设PPT模板

    作为准备进行Java程序员面试的候选人,以下是一些你应该关注和准备的关键领域和...面向对象设计和设计原则:理解面向对象设计的基本原则,如单一职责原则、开闭原则、依赖倒置原则等。能够使用UML图表进行简单的设计。

    达内java培训目录

    理解API设计原则;具备熟练的阅读API文档的能力;为后续的课程学习打下坚实的语言基础。 JavaSE核心 异常处理、多线程基础、IO系统、网络编程、Java反射机制、JVM性能调优(JVM内存结构剖析、GC分析及调优、JVM内存...

    java开源包5

    设计原则 容易维护扩展(不需要修改主类就可以添加新的API支持) 注入型解释器(依据不同的返回格式注入相应的解释器) 集中管理请求参数与参数映射 以运行时异常的方式来管理错误的响应 使用泛型来做强类型编程 多...

    Java2游戏编程.pdf

    附录B 一些常用的Java“要”与“不要”原则 附录C 使用JNI创建一个gamepad阅读器 附录D 使用.JAR文件来部署Java applet 附录E 正确运行Java 2 applet 附录F Magic游戏引擎源码清单 附录G 更多的资源 附录H 配书光盘...

Global site tag (gtag.js) - Google Analytics