该帖已经被评为精华帖
|
|
---|---|
作者 | 正文 |
发表时间:2004-04-01
关于Exception的用法,个人偏好于用Exception来控制流程,就和robbin的像(虽然很多关于Java Performance的文章里面都提到不要这样用).不过由于Exception的性能问题,我们不得不注意一下怎么使用Exception.在程序里面应该同事考虑Performance和结构的问题.好好的利用Exception,分好Exception的类别会对整个产品的结构有很大的帮助.但是唯一要注意的地方就是碰到某些会经常发生的分支(也就是非主流或者异常),我们要避免使用Exception来告诉调用者,因为如果是经常发生的分支也用Exception来表达的话,那么系统的花费一定很大,所以干脆把这些分支也纳入次主流里面,寻找另外的方式来表达.特别是在一些公用的package里面.因为生成一个Exception的花费会与Stack的深度成正比(这里不知道是不是这样表达,反正层数越深就越多),所以一些公用的package里面应该注意这个问题.
我其实也曾经用过boolean和int来做返回的表达方式. 单纯看一个方法的调用以及对其返回的结果的分析的话, int和Exception的code是差不多的. 但是如果一次调用多个方法的话, 就会发现int的代码比较臃肿. 举个例子吧. public int flow(); { int iReturn = -1; iReturn = methodA();; if…… iReturn = methodB();; if…… iReturn = methodC();; if…… return iReturn; } 类似这样的情况,假如methodA,methodB,methodC三种方法返回值所表达的意思有一部分是重复的. 而且他们的后续处理也是一样的情况下, 这样写, 代码会比较臃肿, 即使你把处理代码提取出来. 但利用Exception就可以把同样意思的分支统一处理, 这样看起来、写起来都很舒服. 所以我现在的想法就是发生几率小的分支,就用Exception来表达,利用Exception来控制.如果发生几率比较大的次主流,由于Exception的性能问题,所以避免使用Exception来控制.也就是说某些方法会出现同时利用返回值和Exception来表达运行的情况,主流和次主流的就在返回值里面,分支就在Exception里面. 无论怎样做,个人认为javadoc一定好详细的描述各个返回值和各种Exception的意义,否则就算分类多明确,使用者也有机会感到疑惑的(这我并不想). ps:曾经我也想过让Exception能够比较高效的生成. 开始是pool的概念,后来觉得牺牲太大,决定用clone,简单实在.附件是一个用clone和不用clone的cost的比较的例子(里面的那个Exception比较简单,实际应用的时候可以根据业务需要适当增强). 但是要明白的是,用clone的情况是在这个Exception的StackTrace里面的信息对使用者完全没有意义的时候才使用的. 因为这个CloneException的StackTrace并不是当前的StackTrace.所以觉得用在业务上,代表分支什么的是可以的.也就是说可以利用来作为Business方面的流的控制吧.这样就不会有太大的Performance的损失了. 而且利用Exception时,Performance的损失最大的地方实际上是log Exception的stackTrace的时候(我没有真正用logger测试,单纯用e.printStackTrace()试了一下).到底实际应用中该怎么做,呵呵,还是要自己把握. 多说一句就是不要乱用这个CloneException,有需要才用,希望这个冬冬不会误导别人,感觉是个偏方. 错了请见谅加指点一下 |
|
返回顶楼 | |
发表时间:2004-04-18
个人同意robbin关于异常作为一种流控制的方法,异常带给我们的好处还是多于坏处,至少使得程序更加健壮。
我设计的login总是使用自定义的异常来控制流程,我不同意在调用前检查环境比如if(!userExist())这样只会加重数据库的访问负担,另外,考虑register方法: 如果定义 if(!userExist(...)) { if(register(...)==true) { } } 哈哈问题出来了:且不说访问了两次数据库,如果刚好检测了if,另一个线程了插入了相同的用户名怎么办?难道用synchronize同步?这样的话效率比使用异常还要大大的降低 个人观点,仅供参考。 |
|
返回顶楼 | |
发表时间:2004-04-27
无道 写道 我认为函数抛不抛异常与函数本身的语义密切相关,在函数正常返回的情况下,函数完成了其即定的语义,在返回异常时,说明函数不能顺利地完成语义。因此login函数当然应该有UserNotFoundException异常,因为login函数的语义就是用户登录,而不是看用户是否存在;isUserExist当然就应当返回boolean,因为它就是用来检查用户是否存在的,而不需要抛出异常来表明。
至于使用Checked Exception还UnChecked Exception,我个人认为在OO设计中,可复用性是非常重要的,我们通常希望组件被应用到尽可能广的范围,直接抛出UnChecked Exception将带来不明确性,因为它隐藏了一些信息,不利于复用。 我赞同重视语义的做法,这也是使用OO的好习惯和优美境界. 但是具体到login()这个名称所表达的语义来说,login()的语义比较空泛,这也是导致到底是使用返回值还是异常的分歧点:因为如果login()的内涵界定得纯洁一点,我们就应该使用异常;而如果login()的内涵界定得翔实一点,这时“用户找不到”这样的问题也算是login()语义的一部分,这时就不应该使用异常。所以这个问题不存在对错,而是看分析者本人是如何看待login()的语义内涵的。我认为robbin对于异常的使用过于热情了,因为他定义了大量的自定义异常,这显然有过度设计的嫌疑。 |
|
返回顶楼 | |
发表时间:2004-04-27
现在的企业级应用中,仍旧有使用Exception Code来表示错误的。
不要笑,其实那么多异常的类最终的处理有什么不同?不过是显示一条错误信息罢了,大多数的情况下面,都是不能恢复的。 所以,不要想着什么“落后”,了解本质,更加重要。 |
|
返回顶楼 | |
发表时间:2004-04-28
一个好的肯上进的程序员 不管什么时候都要去考虑自己写的程序的可复用性、健壮性,类及其中方法等是否有清晰明确的目的性,减少与其它的功能模块的耦合。而不能单单的说什么自己在做项目,只为项目的目前情况而考虑,不需要这个不需要那个什么都不考虑。如果真的是这样的话何必要java,何必要oo?自己何时又可以进步?(况且这样做的结果只是短期的看到了节省的时间和开支等,从长远的角度考虑,合理的设计才对项目的成功有着关键的影响)
举个简单的例子 一个权限判断的方法 boolean checkUserPermission(User user, int mId) throws NoExistUserException, NoExistModuleException { } 我并不能保证有谁会使用这个权限判断的方法,但按照面向对象的理论,程序中最好只有一个实现相同功能的方法,所以我定义了不存在用户和不存在模块的异常,这样不管是程序组中谁使用这个方法,就可以清楚的知道这个方法实现的功能,也可以知道出错了是因为什么原因出错也就可以采取相应的方法去处理 所以我支持Robbin的建议 仅仅是为了我们可以写出更好的程序及和伙伴们实现成功的项目 |
|
返回顶楼 | |
发表时间:2004-04-28
gKarerM 写道 现在的企业级应用中,仍旧有使用Exception Code来表示错误的。
不要笑,其实那么多异常的类最终的处理有什么不同?不过是显示一条错误信息罢了,大多数的情况下面,都是不能恢复的。 所以,不要想着什么“落后”,了解本质,更加重要。 不能因为看到烂的代码而也去写烂的代码 每当我看到自己写的方法或类可以被别人重用,我就会觉得很自豪 所以不止是显示一条错误信息那么简单 不是么? :) |
|
返回顶楼 | |
发表时间:2004-06-07
^_^,Exception的确应该理解成事件异常流,应该代表业务逻辑的分枝。
通常遇到系统错误,应该抛出RuntimeException 这个问题,上周我还在分析会上和系统总监争过。 如果调用的外部调用的函数无法处理你抛出的Exception就表示,这种业务分枝无法处理。用系统错误表示就可以了。 robbin的解释更抽象化,看层次看问题,就是清晰啊。 |
|
返回顶楼 | |
发表时间:2004-06-07
Rod Johnson的看法是除了用来进行流程控制的异常外,最好都转换成RuntimeException,除非你自己有能力去处理这个异常。
|
|
返回顶楼 | |
发表时间:2004-06-07
每次throw异常时,虚拟机都会中断操作,然后给堆栈现场拍快照。。。如果程序分支用异常机制做处理。。。大家买“地球模拟器”来运行吧
|
|
返回顶楼 | |
发表时间:2004-06-12
Java 理论与实践: 关于异常的争论
http://www-900.ibm.com/developerWorks/cn/java/j-jtp05254/index.shtml http://www-900.ibm.com/developerWorks/cn/java/j-jtp05254/index_eng.shtml 引用 Rod Johnson 是 J2EE Design and Development (请参阅 参考资料) 的作者,这是我所读过的关于 Java 开发,J2EE 等方面的最好的书籍之一。他采取一个不太激进的方法。他列举了异常的多个类别,并且为每个类别确定一个策略。一些异常本质上是次要的返回代码(它通常指示违反业务规则),而一些异常则是“发生某种可怕错误”(例如数据库连接失败)的变种。Johnson 提倡对于第一种类别的异常(可选的返回代码)使用检查型异常,而对于后者使用运行时异常。在“发生某种可怕错误”的类别中,其动机是简单地认识到没有调用者能够有效地处理该异常,因此它也可能以各种方式沿着栈向上扩散而对于中间代码的影响保持最小(并且最小化异常淹没的可能性)。
引用 我已经发现非检查型异常的最大风险之一就是它并没有按照检查型异常采用的方式那样自我文档化。除非 API 的创建者明确地文档化将要抛出的异常,否则调用者没有办法知道在他们的代码中将要捕获的异常是什么。
引用 Johnson 建议在每个包的基础上选择检查型和非检查型异常。使用非检查型异常时还要记住,即使您并不捕获任何异常,也可能需要使用 try...finally 块,从而可以执行清除动作例如关闭数据库连接。对于检查型异常,我们有 try...catch 用来提示增加一个 finally 子句。对于非检查型异常,我们则没有这个支撑可以依靠。
|
|
返回顶楼 | |