关于这个话题存在很多分歧.在我上一份工作中,我们遇到了一些实际问题,运行时异常被遗忘,直到它们出现在生产中(在agedwards.com上),因此我们决定仅使用已检查的异常.
在我目前的工作中,我发现在很多或所有情况下都有很多人会遇到运行时异常.
这就是我的想法:使用CheckedExceptions,我在编译时被迫至少在调用者中确认异常.对于运行时异常,我不会被编译器强迫,但可以编写一个单元测试,让我处理它.因为我仍然认为越早发现一个bug就越便宜,我就更喜欢CheckedExceptions.
从哲学的角度来看,方法调用是调用者和被调用者之间某种程度的契约.由于编译器强制执行传入的参数类型,因此它似乎是对称的,以便在出路时强制执行类型.也就是说,返回值或异常.
我的经验告诉我,当我使用经过检查的异常时,我获得了更高的质量,也就是代码.检查异常可能会使代码混乱,但有一些技术可以解决这个问题.我喜欢在传递图层边界时翻译异常.例如,如果我从持久层传递,我想将SQL异常转换为持久性异常,因为下一层不应该关心我是否持久化到SQL数据库,但是想要知道是否有东西不能坚持下去.我使用的另一种技术是创建一个简单的异常层次结构.这让我可以编写一层更清晰的代码,因为我可以捕获超类,并且只在真正重要时处理各个子类.
关于这个话题存在很多分歧.在我上一份工作中,我们遇到了一些实际问题,运行时异常被遗忘,直到它们出现在生产中(在agedwards.com上),因此我们决定仅使用已检查的异常.
在我目前的工作中,我发现在很多或所有情况下都有很多人会遇到运行时异常.
这就是我的想法:使用CheckedExceptions,我在编译时被迫至少在调用者中确认异常.对于运行时异常,我不会被编译器强迫,但可以编写一个单元测试,让我处理它.因为我仍然认为越早发现一个bug就越便宜,我就更喜欢CheckedExceptions.
从哲学的角度来看,方法调用是调用者和被调用者之间某种程度的契约.由于编译器强制执行传入的参数类型,因此它似乎是对称的,以便在出路时强制执行类型.也就是说,返回值或异常.
我的经验告诉我,当我使用经过检查的异常时,我获得了更高的质量,也就是代码.检查异常可能会使代码混乱,但有一些技术可以解决这个问题.我喜欢在传递图层边界时翻译异常.例如,如果我从持久层传递,我想将SQL异常转换为持久性异常,因为下一层不应该关心我是否持久化到SQL数据库,但是想要知道是否有东西不能坚持下去.我使用的另一种技术是创建一个简单的异常层次结构.这让我可以编写一层更清晰的代码,因为我可以捕获超类,并且只在真正重要时处理各个子类.
总的来说,我认为Joshua Bloch在Effective Java中的建议最好地总结了您的问题的答案:使用已检查的可恢复条件和编程错误的运行时异常(第2版中的第58项).
所以在这种情况下,如果你真的想要使用异常,它应该是一个经过检查的异常.(除非文档transferTo()
清楚地表明,如果不首先使用其他Account
方法检查足够的平衡,就不能调用该方法 - 但这看起来有点尴尬.)
但另请注意项目59:避免不必要地使用已检查的例外和57:仅在例外条件下使用例外.正如其他人所指出的那样,这个案件可能根本不需要例外.false
如果信用不足,请考虑返回(或者可能是状态对象,其中包含有关所发生事件的详细信息).
何时使用已检查的例外?说实话?以我的拙见......从不.我认为自从我上次创建一个检查异常以来已经过去了大约6年.
你不能强迫某人处理错误.可以说,它使代码变得更糟,而不是更好.我不能告诉你我遇到这样的代码的次数:
try { ... } catch (IOException e) { // do nothing }
虽然我有无数次编写这样的代码:
try { ... } catch (IOException e) { throw new RuntimeExceptione(e); }
为什么?因为一个条件(不一定是IOException;这只是一个例子)是不可恢复的,但是无论如何都被迫放下我的喉咙而且我经常被迫在执行上述操作和污染我的API之间做出选择,只是为了传播一个检查过的异常到顶部它(正确地)致命并将被记录.
Spring的DAO帮助程序类将检查的SQLException转换为未经检查的DataAccessException是有原因的.
如果您对磁盘缺少写入权限,缺少磁盘空间或其他致命条件,您希望尽可能多地产生噪声,并且执行此操作的方法是使用...未经检查的异常(甚至是错误).
此外,已检查的异常会破坏封装.
检查异常的这个想法应该被用于"可恢复的"错误,这实际上是一种天空般的一厢情愿的想法.
Java中检查的异常是一个实验......一个失败的实验.我们应该减少损失,承认我们犯了错误并继续前进.恕我直言.Net通过仅具有未经检查的异常使其正确.然后,它再次获得了从Java的错误中学习的第二个优势.
恕我直言,它应该不是一个例外.在我看来,应该在特殊情况发生时使用异常,而不是作为流控制.
在你的情况下,有人试图转移比余额允许更多的钱,这并不是一个特殊的状态.我认为这些事情在现实世界中经常发生.所以你应该针对这些情况进行编程.一个例外可能是你的if语句评估余额是好的,但是当实际从账户中扣除钱时,由于一些奇怪的原因,余额不再好了.
例外情况可能是,在调用之前transferTo()
,您检查了该行是否对银行开放.但在内部transferTo()
,代码注意到该行不再打开,但是,按照所有逻辑,它应该是.这是一个例外.如果线路无法打开,那也不例外,这是一个看似合理的情况.
恕我直言回顾:例外==奇怪的黑魔法.
是建设性编辑:
所以,不要太矛盾,方法本身可能会抛出异常.但是应该控制方法的使用:首先检查余额(transferTo()
方法之外),如果余额好,则只调用transferTo()
.如果transferTo()
注意到,由于某种奇怪的原因,余额不再是好的,你会抛出你努力捕获的异常.
在这种情况下,你可以连续使用所有的鸭子,并且知道除了记录异常,向某人发送通知并告诉客户之外,您无法做更多的事情(因为它本身就true
变成了什么false
)礼貌地说,有人在上一次满月时没有正确地牺牲他们的处女,问题将在第一个可能的时刻得到解决.
不太enterprisey暗示编辑:
如果你这样做是为了你自己的乐趣(并且情况似乎是这样,请参阅注释),我建议返回一个布尔值.用法是这样的:
// ... boolean success = transferTo(otherAccount, ONE_MILLION_DOLLARS_EXCLAMATION); if (!success) { UI.showMessage("Aww shucks. You're not that rich"); return; // or something... } else { profit(); } // ...
我最近遇到了异常问题,代码抛出了NullPointerException,我不知道为什么,经过一些调查后发现真正的异常被吞噬了(它是在新代码中,所以它仍在完成)并且方法只返回null.如果你检查了异常,你必须明白坏程序员只会尝试捕获它并忽略异常.
我的规则是
if语句的业务逻辑错误(如代码)
应用程序可以恢复的环境错误的异常
没有恢复的环境错误的未经检查的异常
已检查例外的示例:对于可脱机工作的应用程序,网络已关闭
未经检查的异常的示例:数据库在CRUD Web应用程序上关闭.
有很多关于这个主题的文档.您可以通过浏览Hibernate网页找到很多,因为他们在版本3.x中将Hibernate 2.x的所有异常从已检查更改为未选中
从未经检查的例外 - 争议:
如果可以合理地期望客户端从异常中恢复,则将其作为已检查的异常.如果客户端无法执行任何操作以从异常中恢复,请将其设置为未经检查的异常.
请注意,未经检查的异常是派生自RuntimeException
的异常,并且已检查的异常是派生自的异常Exception
.
RuntimeException
如果客户端无法从异常中恢复,为什么抛出?文章解释说:
运行时异常表示编程问题导致的问题,因此,无法合理地期望API客户端代码从它们恢复或以任何方式处理它们.这些问题包括算术异常,例如除以零; 指针异常,例如尝试通过空引用访问对象; 和索引异常,例如尝试通过索引太大或太小来访问数组元素.
我的感觉是,经过检查的例外是一个有用的合同,应该谨慎使用.我认为检查异常是一个好主意的经典示例是InterruptedException.我的感觉是,我希望能够在我希望它停止时停止线程/进程,无论有人指定Thread.sleep()多长时间.
所以,试图回答你的具体问题,这是你绝对想要确保每个人都处理的问题吗?在我看来,一个Account
没有足够资金的情况是一个严重的问题,你必须处理它.
回应Peter的评论:这是一个使用InterruptedException作为应该处理的异常的具体情况的例子,你需要有一个有用的默认处理程序.这是我强烈推荐的,当然是在我的实际工作中.你应该至少这样做:
catch (InterruptedException ie) { Thread.currentThread().interrupt(); }
该处理程序将确保代码捕获已检查的异常并完全按照您的要求执行:使此线程停止.不可否认,如果上游还有另一个异常处理程序/食者,那么它将不太可能处理异常.即便如此,FindBugs可以帮助您找到这些.
现在,现实开始了:你不一定强迫每个为你的检查异常编写异常处理程序的人都能很好地处理它.也就是说,至少你可以"查找用法"并知道它的用途并给出一些建议.
简短形式:如果使用已检查的异常,则会对方法的用户造成负担.确保有充分的理由,建议正确的处理方法,并广泛记录所有这些.