论坛首页 Java企业应用论坛

为什么 Java 中要使用 Checked Exceptions

浏览 193221 次
该帖已经被评为精华帖
作者 正文
   发表时间:2004-06-14  
花了点时间看了上面的讨论,感觉受益不少!
每个人在站不同的角度,都能说出相应的道理。事实也是如此!
个人认为java引入checked Exceptions是很有必要的,而不仅仅是throws了事,你必须在设计(useCase)的时候就要尽可能考虑到会发生的异常,异常不是错误。
定义的越详细越好,而不是Exception概之!
0 请登录后投票
   发表时间:2004-06-21  
何谓异常?
1、 程序正常控制流之外、不能完成函数语义的事件称为异常。
2、 何谓异常,判断的依据是服务的定义,导致不能完成服务的事件就是异常(虽然说从服务提供者与客户的角度上各有不同,但其实是双方对服务的内涵理解不一致,属于契约与交流问题)。
3、 因为不满足服务的所需条件而发生异常事件。这些条件或是参数的非法、或是内存不足、或是网络调用失败等等。


异常的使用:
1、 发生异常一定要抛出,既然是异常,必然是内部无法消化的事件,不可能也不应该在内部消化。
【Effective Java】不要忽略异常。
2、 抛出的异常要语义明确。这需要对异常进行语义转换与包装,尤其是在层与层之间的异常转换。其实,JDK中出现异常层次结构,主要目的就是使语义明确。
3、 服务者内部实现中,不能通过异常来控制流程;但在服务提供者与客户之间的合作来说,有时候,在客户方会有根据不同异常采取不同行为。但异常的根本目的是提供清晰的语义,而不是控制流程。


Checked Exception与UnChecked Exception:
1、 抛出Checked Exception,给直接客户施加一个约束,必须处理,但也是一种自由,客户可分门别类的处理不同异常;
UnChecked Exception则给直接客户以自由,但也是一种欺瞒,因为客户不知道将要发生什么,所有的处理将是系统默认的处理(如打印堆栈到控制台,对开发者、用户都返回一样的内容,不管别人懂与不懂)。
二者的选择其实是约束与自由的权衡。
2、 “对可恢复的情况使用已检查异常,对程序错误使用运行时异常。”而不是一咕脑的全抛出Checker Exception,这服务提供者是友好的、“敏捷的”^_^。
3、 所以,若不需要客户依据不同异常采取不同后续行为,那么抛出UnChecked Exception是友好的;但若客户需要根据不同异常类采取不同行动,抛出Checked Exception是友好的。
0 请登录后投票
   发表时间:2004-06-21  
baichenhong 写道
Rod Johnson的看法是除了用来进行流程控制的异常外,最好都转换成RuntimeException,除非你自己有能力去处理这个异常。


spring 中对SQLException 的封装体现了这一点,但rod johnson 也这样说
引用

笔者采取了一种比eckel 稍正统的观点,因为笔者认为已检查异常有一定用武之地,在一个异常相当于来自方法的一个可替代返回值得地方,这个异常无疑应该被检查,并且该语言能帮助实施这一点就再好不过了。但是,笔者觉得传统的java方法过分强调了已检查异常。

已检查异常要比错误返回码(许多老式的语言中使用)好很多。迟早(或许不久),人们将不能检查一个错误返回值;使用编译程序来实施正确的错误处理时一件好事。同参数和返回值一样,这样的已检查异常对一个对象的api来说是整体的不可分部分



这可以说明robbin的观点,还是有许多人认同的
0 请登录后投票
   发表时间:2004-08-21  
robbin说的是一种实现方式,也是有一定道理的,我以前也是多次使用这种方式在应用开发中。不过,根据我的经验,更倾向于potain的那种想法。如果你已经站在系统的最上层,我想两种方式都可以,性能也没有多大区别,但是如果你位于底层或者中间某层的话,最好还是少使用exception为妙,性能是肯定受影响的。我本人更喜欢把业务流程分成3类,main flow,additional flow和exception flow。这样更加清晰,也可避免robbin的后两种flow合一的处理模式。
一家之见,抛砖引玉。
0 请登录后投票
   发表时间:2004-08-25  
我个人不同意robbin的说法。这种说法太偏激。
  boolean checkUser(sUsername,sPass)有什么不可以?这就不是面向对象编程?
  异常在一个项目中,应该处于是去处理问题的辅助地位,而不是去融于业务设计的主导地位。
0 请登录后投票
   发表时间:2004-08-25  
我是异常的坚定支持者。对为了效率而放弃异常的行为嗤之以鼻。
异常出现的几率是很小的,所以根本没有必要担心效率。

不过checkUser还是返回boolean顺眼。
毕竟,异常表示的是“异常”,不是用在正常逻辑之中。

我的checked和nonchecked异常的标准:
1。如果一个异常的出现意味着bug,debug好的系统不应该出现这个问题(比如很多输入不符合要求什么的),那么就是runtime.
2。如果一个异常的出现是合理的,并且用户需要对这个异常进行恢复,那么就用checked exception。比如IOException, FileNotFoundException等等。
3。一个比较一般性的异常,用户拿到了也基本上没有恢复的能力的异常,用runtime。这点上,感觉SQLException, RemoteException设计的不好。

Anders的观点我还是部分认同的。
所谓的异常会累积,我想主要出现在分层次的系统中。
比如一个经典的设计:
ui层 - 业务层 - 持久层 - 物理层。

物理层会throw SQLException,而这个exception明显不应该被任何中间层所截获处理,而应该一直抛到最上层。
但是,如果你在持久层声明了throws SQLException,你的使用jdbc的实现细节就暴露了。
所以,最好的办法是catch住,然后封装成一个持久层的exception。同样如果物理层还会抛FileNotFoundException, RemoteException等等,这些都是物理层的实现细节,都要在持久层封装。
如果持久层封装的时候继续用checked exception,基于同样的道理上面一层还是要继续封装。

这样的层层封装累不累呀?有必要吗?


我想anders的另一个观点在于:exception spec复杂化了接口签名,复杂化了语言。很明显的一点,引入了generics的话,exception spec就让情况复杂很多。
总而言之,在有得有失,利弊难说的时候,保持简单也许是一个从长远角度考虑的正确决定。加一个东西永远比去掉一个东西容易。(当然,眼前确实可能会让软件不那么健壮了)
0 请登录后投票
   发表时间:2004-08-25  
ajoo 写道

这样的层层封装累不累呀?有必要吗?


spring中这个部分因而也变成了一个特色,大部分包中的异常通常再封装一次
0 请登录后投票
   发表时间:2004-08-26  
更正一下,没有看清楚robbin的上下文就胡乱评价了. 该死.

我完全同意robbin的关于loginUser的设计方法.

如果只有用户存在不存在的简单情况,自然用返回值多快好省.

但是,当可能的错误很多的时候,比如:
用户不存在,
用户存在但是被封
密码错误

等等等等, 自然是用exception更好.
用exception的好处是错误信息是结构化的,并且错误是可以任意扩展的.

比如,用户被删除了, 我甚至可以报告用户被删除了几天.
用户被封了,我甚至可以报告用户被哪个该死的斑竹封的,什么时候封的,封了几天等等.
这些,你拿返回值来玩?

这个exception是否是runtime的倒是值得商讨.
runtime的缺点是无法强制客户代码处理.
checked的缺点是如果加入了新的exception种类,客户代码必须马上改动, 如果中间有很多层,就要层层上报,即使这个exception暂时可能不会出现.
(比如系统封人制度还没有启动)

各有利弊. 一般来说我会让UserNotExistException之类的作为checked exception, 一些不那么重要的exception也许就可以作为runtime.
0 请登录后投票
   发表时间:2004-08-26  
我选择采用robbin和potian的方法的标准是:
如果有一个高效的检测方法,当通过检测时主方法保证可以不抛出异常工作,我认为程序员有责任在调用这个主方法之前做检测,而主方法在失败时抛出Runtime Exception(可能有些非议,不过这里主要是逃过java的catch规定:我既然可以保证不抛出,还在那里catch,实在多余),这就如同是IllegalArgumentException:一般你在传入之前可以自己查一查吧;反之(比如这个login吧,如果有一个同步的问题等),检测方法还是提供,但抛出的异常改为Checked Exception来强制检查,也就是所谓的“返回值类”Exception。

这个设计的选择就要到涉及具体问题的时候再做了。
0 请登录后投票
   发表时间:2004-08-26  
使用Checked Exception还是UnChecked Exception的原则,我的看法是根据需求而定。

如果你希望强制你的类调用者来处理异常,那么就用Checked Exception;
如果你不希望强制你的类调用者来处理异常,就用UnChecked。

那么究竟强制还是不强制,权衡的依据在于从业务系统的逻辑规则来考虑,如果业务规则定义了调用者应该处理,那么就必须Checked,如果业务规则没有定义,就应该用UnChecked。

还是拿那个用户登陆的例子来说,可能产生的异常有:

IOException (例如读取配置文件找不到)
SQLException (例如连接数据库错误)
ClassNotFoundException(找不到数据库驱动类)

NoSuchUserException
PasswordNotMatchException

以上3个异常是和业务逻辑无关的系统容错异常,所以应该转换为RuntimeException,不强制类调用者来处理;而下面两个异常是和业务逻辑相关的流程,从业务实现的角度来说,类调用者必须处理,所以要Checked,强迫调用者去处理。

在这里将用户验证和密码验证转化为方法返回值是一个非常糟糕的设计,不但不能够有效的标示业务逻辑的各种流程,而且失去了强制类调用者去处理的安全保障。

至于类调用者catch到NoSuchUserException和PasswordNotMatchException怎么处理,也要根据他自己具体的业务逻辑了。或者他有能力也应该处理,就自己处理掉了;或者他不关心这个异常,也不希望上面的类调用者关心,就转化为RuntimeException;或者他希望上面的类调用者处理,而不是自己处理,就转化为本层的异常继续往上抛出来。
0 请登录后投票
论坛首页 Java企业应用版

跳转论坛:
Global site tag (gtag.js) - Google Analytics