`
luckaway
  • 浏览: 138777 次
  • 性别: 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(原文)
分享到:
评论
16 楼 shadowlin 2011-01-04  
badqiu 写道
luckaway 写道
badqiu 写道
不使用异常的人员一直扛着两面大旗:
1. "不要使用异常来控制流程"
2. "性能"

LZ的回答很好,回答了异常最主要的功能:分离错误代码和正常代码,代码更简洁。

但至于 checked exceptin,unchedked exceptin. 你就不要再纠结了,统一使用 RuntimeException. checked exception是JAVA最SB的设计.

1. 它要求调用方强制在调用的位置处理异常. 如SQLException, RMI RemoteException,其实调用方也没有任何办法处理异常,只能log或者再包装一次抛出去. 
2. 影响接口,如果你的接口已经发布出去,如果想增加异常抛出怎么办呢? 使用checked exceptin要影响客户端的代码,编译都过不了. 所以折衷的办法是在接口声明一个所有的父类异常. 如ant的 BuildException.


并且现在的 J2EE规范也基本不使用 checked exception
多数情况下JDK自带的CheckedException,我们都会转化成Unchecked Exception的,如果JDK,如果JDK异常全部都转成Unchecked Exception,异常携带的信息不如我们自己控制更丰富,更灵活。

一些业务异常还是需要CheckException的,程序要根据不同的异常类型返回给用户不同的信息!

比如说,金币不够的异常,你要告诉用户金币不够,还要明确的告诉他现在所以剩多少金币,还需要多少金币,甚至还引导用户到金币充值流程(页面上让用户作出选择),这些都是Unchecked Exception无法做到的。

当然还有另一种解决方案,实现一个金币是否足够的方法! 用哪种实现方法的依据是“金币不够”是属于异常流程还是正常流程。



没搞明白你,难道你 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);
    }
}




这个可以在service层就把异常都处理掉吧。如果这个checked的异常是很无聊的一个异常的话只能说是设计上面的问题,不能说是checked异常的问题。checked异常的用处不就是告诉你我这里可能会出错么,如果都是runtime的话对于其他人连你可能会有哪些异常都不知道啊。
15 楼 badqiu 2011-01-04  
luckaway 写道
badqiu 写道
不使用异常的人员一直扛着两面大旗:
1. "不要使用异常来控制流程"
2. "性能"

LZ的回答很好,回答了异常最主要的功能:分离错误代码和正常代码,代码更简洁。

但至于 checked exceptin,unchedked exceptin. 你就不要再纠结了,统一使用 RuntimeException. checked exception是JAVA最SB的设计.

1. 它要求调用方强制在调用的位置处理异常. 如SQLException, RMI RemoteException,其实调用方也没有任何办法处理异常,只能log或者再包装一次抛出去. 
2. 影响接口,如果你的接口已经发布出去,如果想增加异常抛出怎么办呢? 使用checked exceptin要影响客户端的代码,编译都过不了. 所以折衷的办法是在接口声明一个所有的父类异常. 如ant的 BuildException.


并且现在的 J2EE规范也基本不使用 checked exception
多数情况下JDK自带的CheckedException,我们都会转化成Unchecked Exception的,如果JDK,如果JDK异常全部都转成Unchecked Exception,异常携带的信息不如我们自己控制更丰富,更灵活。

一些业务异常还是需要CheckException的,程序要根据不同的异常类型返回给用户不同的信息!

比如说,金币不够的异常,你要告诉用户金币不够,还要明确的告诉他现在所以剩多少金币,还需要多少金币,甚至还引导用户到金币充值流程(页面上让用户作出选择),这些都是Unchecked Exception无法做到的。

当然还有另一种解决方案,实现一个金币是否足够的方法! 用哪种实现方法的依据是“金币不够”是属于异常流程还是正常流程。



没搞明白你,难道你 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);
    }
}


14 楼 whaosoft 2011-01-04  
joknm 写道
yanical 写道
不要用异常来控制程序的正常流程


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

可我们经常用异常来控制程序的正常流程
13 楼 luckaway 2011-01-04  
Vicent_Lee 写道
现在看的都有些懵了,按我的理解,不管是Exception及其子类或者RuntimeException及其子类,都是有可能对你的业务逻辑产生影响的,如果你非常坚信你的程序不会出错,当然你不用管它是什么异常,但是如果他影响了你的业务逻辑,对用户的体验产生了负面影响,那你就得去解决,当用户访问资源时出现问题,怎么可能不对产生这样的问题的异常进行处理,不能一味的追求代码上的整洁。


是都用进行处理,不可能把异常抛给用户的!

是如何处理的问题,造成运行时异常的原因有很多,比如;数据库连不上,远程地址访问,数据越界,空指针等,这些根据业务是没什么关系的,不可能根据异常类型一一给出不同的提示。可能就是在Action或者一个统一错误处理类,记录错误的日志,然后提示用户“系统错误,请联系管理员”之类的

对这类异常用户是无能为力的

12 楼 Vicent_Lee 2011-01-04  
现在看的都有些懵了,按我的理解,不管是Exception及其子类或者RuntimeException及其子类,都是有可能对你的业务逻辑产生影响的,如果你非常坚信你的程序不会出错,当然你不用管它是什么异常,但是如果他影响了你的业务逻辑,对用户的体验产生了负面影响,那你就得去解决,当用户访问资源时出现问题,怎么可能不对产生这样的问题的异常进行处理,不能一味的追求代码上的整洁。
11 楼 luckaway 2011-01-04  
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值
10 楼 luckaway 2011-01-04  
badqiu 写道
不使用异常的人员一直扛着两面大旗:
1. "不要使用异常来控制流程"
2. "性能"

LZ的回答很好,回答了异常最主要的功能:分离错误代码和正常代码,代码更简洁。

但至于 checked exceptin,unchedked exceptin. 你就不要再纠结了,统一使用 RuntimeException. checked exception是JAVA最SB的设计.

1. 它要求调用方强制在调用的位置处理异常. 如SQLException, RMI RemoteException,其实调用方也没有任何办法处理异常,只能log或者再包装一次抛出去. 
2. 影响接口,如果你的接口已经发布出去,如果想增加异常抛出怎么办呢? 使用checked exceptin要影响客户端的代码,编译都过不了. 所以折衷的办法是在接口声明一个所有的父类异常. 如ant的 BuildException.


并且现在的 J2EE规范也基本不使用 checked exception
多数情况下JDK自带的CheckedException,我们都会转化成Unchecked Exception的,如果JDK,如果JDK异常全部都转成Unchecked Exception,异常携带的信息不如我们自己控制更丰富,更灵活。

一些业务异常还是需要CheckException的,程序要根据不同的异常类型返回给用户不同的信息!

比如说,金币不够的异常,你要告诉用户金币不够,还要明确的告诉他现在所以剩多少金币,还需要多少金币,甚至还引导用户到金币充值流程(页面上让用户作出选择),这些都是Unchecked Exception无法做到的。

当然还有另一种解决方案,实现一个金币是否足够的方法! 用哪种实现方法的依据是“金币不够”是属于异常流程还是正常流程。
9 楼 joknm 2011-01-04  
yanical 写道
不要用异常来控制程序的正常流程


LZ一语道破。
最近也很纠结这个问题。
8 楼 yanical 2011-01-03  
luckaway 写道

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

不错。不过比较疑惑的还是什么时候用Checked Exception,什么时候用Un-Checked Exception。
一直赞同的一个观点是:只有在客户端可以通过某些手段来弥补发生的错误时才使用Checked Exception。
似乎楼主也支持这个观点?但是我一直没有发现过那个地方可以确定客户端可以弥补错误,包括楼主举的上面这个例子,我觉得这个时候客户端也没法做什么了。而且为了这种小可能做成Checked Exception,让调用的地方都要来处理,好像把代码搞混乱了。
7 楼 badqiu 2011-01-03  
downpour 写道
badqiu 写道
不使用异常的人员一直扛着两面大旗:
1. "不要使用异常来控制流程"
2. "性能"

LZ的回答很好,回答了异常最主要的功能:分离错误代码和正常代码,代码更简洁。

但至于 checked exceptin,unchedked exceptin. 你就不要再纠结了,统一使用 RuntimeException. checked exception是JAVA最SB的设计.

1. 它要求调用方强制在调用的位置处理异常. 如SQLException, RMI RemoteException,其实调用方也没有任何办法处理异常,只能log或者再包装一次抛出去. 
2. 影响接口,如果你的接口已经发布出去,如果想增加异常抛出怎么办呢? 使用checked exceptin要影响客户端的代码,编译都过不了. 所以折衷的办法是在接口声明一个所有的父类异常. 如ant的 BuildException.


并且现在的 J2EE规范也基本不使用 checked exception


我觉得你的评论实在是太绝对了。Checked Exception到底有没有存在的必要?这个问题你能论证清楚么?什么叫做Checked Exception是Java最SB的设计?我恰恰认为Java中区分Exception和RuntimeException是Java中比较经典的设计。因为从设计层面上说,它提供了设计者一种必要的语法层面的提醒:你是否应该在某个接口或者实现类中处理可能发生的潜在问题?

所谓Checked Exception,更多需要从Java的语法角度去理解:

第一,一个接口方法要抛出Checked Exception,表明这个接口定义向外表达的含义是:请大家注意,在我的实现中,可能会产生异常,但是这类异常与我自身的实现机制关系不大,可能更多是由于调用方式,或者一些外部因素的错误引起的,所以在我的实现内部我就不处理了,谁调用我,谁负责处理。

第二,Checked Exception能够与Unchecked Exception区分的一个重要原因是在一个完整的流程中,Unchecked Exception是可控的,可参与业务逻辑封装的。而Checked Exception更多时候是不可控的,是无法参与业务逻辑的。比如说,在Spring中,RuntimeException及其子类,在数据库操作中会引起事务的回滚,然而Exception的子类却会被事务操作忽略。

是否需要将Checked Exception转化为Unchecked Exception,在一定程度上取决于设计者的素质,并非是绝对的。我们所追求的,是一种统一的代码逻辑和规则,只要在整个团队中统一称为一个标准,那么很多设计上的纰漏也不是问题。




这是我的观点, 所以着重 红色加粗强调. 

我则认为一定需要将 checked exception转换为 unchecked exception.这才是最基本的素质. 因为 checked exception污染很严重, 如SQLException,你在调用的最初位置不转换为RuntimeException或log处理,那么你一定就要抛出去,但这又会污染你的调用方, 所以一级级,所有调用该方法的都需要声明抛出该异常. 难道这就是你想要的?


对于资源类或远程RPC操作,是一定会发生异常的,在程序上肯定会加以考虑资源的关闭等操作来处理异常. 根本不需要你使用一个语法检查来强制要求我在调用的位置处理异常.
6 楼 downpour 2011-01-03  
badqiu 写道
不使用异常的人员一直扛着两面大旗:
1. "不要使用异常来控制流程"
2. "性能"

LZ的回答很好,回答了异常最主要的功能:分离错误代码和正常代码,代码更简洁。

但至于 checked exceptin,unchedked exceptin. 你就不要再纠结了,统一使用 RuntimeException. checked exception是JAVA最SB的设计.

1. 它要求调用方强制在调用的位置处理异常. 如SQLException, RMI RemoteException,其实调用方也没有任何办法处理异常,只能log或者再包装一次抛出去. 
2. 影响接口,如果你的接口已经发布出去,如果想增加异常抛出怎么办呢? 使用checked exceptin要影响客户端的代码,编译都过不了. 所以折衷的办法是在接口声明一个所有的父类异常. 如ant的 BuildException.


并且现在的 J2EE规范也基本不使用 checked exception


我觉得你的评论实在是太绝对了。Checked Exception到底有没有存在的必要?这个问题你能论证清楚么?什么叫做Checked Exception是Java最SB的设计?我恰恰认为Java中区分Exception和RuntimeException是Java中比较经典的设计。因为从设计层面上说,它提供了设计者一种必要的语法层面的提醒:你是否应该在某个接口或者实现类中处理可能发生的潜在问题?

所谓Checked Exception,更多需要从Java的语法角度去理解:

第一,一个接口方法要抛出Checked Exception,表明这个接口定义向外表达的含义是:请大家注意,在我的实现中,可能会产生异常,但是这类异常与我自身的实现机制关系不大,可能更多是由于调用方式,或者一些外部因素的错误引起的,所以在我的实现内部我就不处理了,谁调用我,谁负责处理。

第二,Checked Exception能够与Unchecked Exception区分的一个重要原因是在一个完整的流程中,Unchecked Exception是可控的,可参与业务逻辑封装的。而Checked Exception更多时候是不可控的,是无法参与业务逻辑的。比如说,在Spring中,RuntimeException及其子类,在数据库操作中会引起事务的回滚,然而Exception的子类却会被事务操作忽略。

是否需要将Checked Exception转化为Unchecked Exception,在一定程度上取决于设计者的素质,并非是绝对的。我们所追求的,是一种统一的代码逻辑和规则,只要在整个团队中统一称为一个标准,那么很多设计上的纰漏也不是问题。
5 楼 badqiu 2011-01-03  
不使用异常的人员一直扛着两面大旗:
1. "不要使用异常来控制流程"
2. "性能"

LZ的回答很好,回答了异常最主要的功能:分离错误代码和正常代码,代码更简洁。

但至于 checked exceptin,unchedked exceptin. 你就不要再纠结了,统一使用 RuntimeException. checked exception是JAVA最SB的设计.

1. 它要求调用方强制在调用的位置处理异常. 如SQLException, RMI RemoteException,其实调用方也没有任何办法处理异常,只能log或者再包装一次抛出去. 
2. 影响接口,如果你的接口已经发布出去,如果想增加异常抛出怎么办呢? 使用checked exceptin要影响客户端的代码,编译都过不了. 所以折衷的办法是在接口声明一个所有的父类异常. 如ant的 BuildException.


并且现在的 J2EE规范也基本不使用 checked exception
4 楼 luckaway 2011-01-03  
zyongking 写道
没有更新啊,我看到的还是错误的。

哪里有错啊?都已经改过来了!
3 楼 zyongking 2011-01-03  
没有更新啊,我看到的还是错误的。
2 楼 luckaway 2011-01-01  
gaoshuaidage 写道
* Unchecked Exception: Exception的子类,方法签名上需要显示的声明throws,编译器迫使调用者处理这类异常或者声明throws继续往上抛。
* Checked Exception: RuntimeException的子类,方法签名不需要声明throws,编译器也不会强制调用者处理该类异常。


这个是不是反了。

谢谢,已经改过来了!
1 楼 gaoshuaidage 2011-01-01  
* Unchecked Exception: Exception的子类,方法签名上需要显示的声明throws,编译器迫使调用者处理这类异常或者声明throws继续往上抛。
* Checked Exception: RuntimeException的子类,方法签名不需要声明throws,编译器也不会强制调用者处理该类异常。


这个是不是反了。

相关推荐

    有效处理JAVA异常三原则

    为了深入理解和正确实施这一机制,本文将阐述有效处理Java异常的三个重要原则,并结合JCheckbook类的示例进行讨论。 首先,Java中的异常是由Throwable类的层次结构所定义的,其中包含了Error、Exception以及...

    Java异常框架设计

    8. **异常设计原则** - **异常应该具有明确的语义**:异常的命名和类型应清楚地表明它代表的错误情况。 - **避免在finally块中抛出异常**:这可能导致原始异常被覆盖,丢失重要信息。 - **不要滥用异常**:异常不...

    Java论文异常处理论文:JAVA异常处理的分析与研究.doc

    Java 异常处理是 Java 语言的一大特色,该文在介绍 Java 语言的异常处理技术的基础上,提出了异常处理的设计原则,从而为 Java 的程序设计实现提供技术上的借鉴。 Java 异常处理机制的主要方法是利用面向对象技术,...

    Java并发编程设计原则和模式

    本资料“Java并发编程设计原则和模式”深入探讨了如何在Java环境中有效地进行并发处理,以充分利用系统资源并避免潜在的并发问题。 一、并发编程基础 并发是指两个或多个操作在同一时间段内执行,但并不意味着这些...

    Java异常处理终结篇——如何进行Java异常处理设计 - 望远的个人页面 - 开源中国社区1

    在进行Java异常处理设计时,我们需要遵循以下原则: 1. **不要直接忽略异常**:捕获到的异常应当被适当处理,无论是记录日志、通知用户还是尝试恢复。忽略异常可能会导致程序行为不可预测,甚至导致程序崩溃。 2. ...

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

    以下是基于给定内容总结的Java异常处理的设计原则: 1. **早处理**:异常不应该用于控制正常程序流程,而应该仅用于处理非预期的情况。一旦发生异常,应尽早捕获并处理,避免异常向上冒泡到程序的更高层次。如果不...

    Java并发编程_设计原则和模式(CHM)

    本资源"Java并发编程_设计原则和模式(CHM)"聚焦于Java语言在并发环境下的编程技巧、设计原则以及最佳实践模式。 一、并发编程基础 并发编程涉及多个执行单元同时运行,这些单元可能是线程或进程。在Java中,主要...

    高效的java异常处理框架高效的java异常处理框架高效的java异常处理框架

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

    java异常(pdf格式)

    ### Java异常详解 #### 一、异常的基本概念 在Java编程中,“异常”是指程序运行过程中发生的非预期事件,这些事件可能会导致程序无法正常执行。Java中的异常处理机制旨在帮助开发者更好地控制和处理这些异常情况...

    java 程序设计思想

    本书的核心目的是帮助新手程序员理解和掌握这些设计原则和最佳实践,从而提升他们的编程水平和思维方式。下面将详细讨论Java程序设计中的关键知识点。 1. **面向对象编程(OOP)**:Java是一种面向对象的语言,其...

    论文研究-基于Java异常处理机制的研究 .pdf

    Java异常处理机制研究的知识点涵盖了异常处理的基本概念、分类、原则以及实际应用等方面。 1. 异常处理概念 异常处理是Java语言中用于处理程序运行时遇到的错误和异常情况的一种机制。它通过异常类的层次结构来实现...

    java程序设计课件

    遵循SOLID原则,编写可读性强、可维护的代码,以及了解如何利用设计模式解决常见问题,都是成为一个优秀Java程序员的必备素养。 总的来说,天津大学的这门Java程序设计课件涵盖了从基础知识到进阶主题的全面内容,...

    Java异常处理机制应用研究.pdf

    本文将详细介绍 Java 异常处理机制的应用研究,包括 Java 异常体系统结构、异常分类与处理机制、异常处理的一般原则和异常处理框架等。 Java 异常体系统结构 Java 异常体系统结构如图 1 所示,Throwable 是所有...

    JAVA异常详解.doc

    Java异常体系是基于面向对象的设计,使得错误处理更加结构化和可维护。以下是Java异常的一些主要类别和相关知识点: 1. **算术异常**:`ArithmeticException` - 当发生违反算术规则的操作时,例如整数除以零,会抛...

    JAVA程序设计课程设计

    清晰的注释、合理的命名、有效的代码复用以及遵循SOLID原则的类设计,都是提高代码质量和可维护性的关键。 通过这个Java程序设计课程设计,学生不仅能提升编程技能,还能锻炼解决问题的能力,为未来的职业生涯打下...

    JAVA课程设计报告--记事本.rar

    在本次的JAVA课程设计中,学生们被要求实现一个基本的记事本程序,这是一项常见的实践任务,旨在加深对Java编程语言的理解,并提升面向对象编程、事件处理以及GUI(图形用户界面)设计的能力。这份“JAVA课程设计...

    Java异常处理机制及应用.pdf

    Java 异常处理原则是指在处理异常时需要遵守的一些基本原则,如尽量少地抛出异常、合理地使用 finally 块、避免 catch-all 异常、使用多 catch 块等。 五、Java 异常处理机制的应用 Java 异常处理机制的应用非常...

    java API设计

    1. **设计原则**:遵循SOLID原则(单一职责原则、开闭原则、里氏替换原则、接口隔离原则和依赖倒置原则),这有助于创建稳定、可复用的API。此外,还包括DRY(Don't Repeat Yourself)原则,避免重复代码。 2. **...

    java开发异常处理与日志规范

    2. **异常不应用于流程控制**:异常设计的目的是处理程序运行中的意外情况,而不是作为常规的流程控制手段。使用if语句进行条件判断通常更有效率且更清晰。 3. **区分稳定代码和非稳定代码**:在catch块中,应将...

    《面向对象程序设计JAVA语言程序设计》

    《面向对象程序设计JAVA语言程序设计》涵盖了 Java 语言的基础知识点、设计原则、编程风格、多线程和并发、网络和 Web 编程、异常处理、安全和可靠性、Applet 和应用程序、设计模式、测试和调试等多方面的内容。

Global site tag (gtag.js) - Google Analytics