当前位置:  开发笔记 > 编程语言 > 正文

在Java中,何时应该创建一个已检查的异常,何时应该是运行时异常?

如何解决《在Java中,何时应该创建一个已检查的异常,何时应该是运行时异常?》经验,为你挑选了8个好方法。

关于这个话题存在很多分歧.在我上一份工作中,我们遇到了一些实际问题,运行时异常被遗忘,直到它们出现在生产中(在agedwards.com上),因此我们决定仅使用已检查的异常.

在我目前的工作中,我发现在很多或所有情况下都有很多人会遇到运行时异常.

这就是我的想法:使用CheckedExceptions,我在编译时被迫至少在调用者中确认异常.对于运行时异常,我不会被编译器强迫,但可以编写一个单元测试,让我处理它.因为我仍然认为越早发现一个bug就越便宜,我就更喜欢CheckedExceptions.

从哲学的角度来看,方法调用是调用者和被调用者之间某种程度的契约.由于编译器强制执行传入的参数类型,因此它似乎是对称的,以便在出路时强制执行类型.也就是说,返回值或异常.

我的经验告诉我,当我使用经过检查的异常时,我获得了更高的质量,也就是代码.检查异常可能会使代码混乱,但有一些技术可以解决这个问题.我喜欢在传递图层边界时翻译异常.例如,如果我从持久层传递,我想将SQL异常转换为持久性异常,因为下一层不应该关心我是否持久化到SQL数据库,但是想要知道是否有东西不能坚持下去.我使用的另一种技术是创建一个简单的异常层次结构.这让我可以编写一层更清晰的代码,因为我可以捕获超类,并且只在真正重要时处理各个子类.



1> Don Branson..:

关于这个话题存在很多分歧.在我上一份工作中,我们遇到了一些实际问题,运行时异常被遗忘,直到它们出现在生产中(在agedwards.com上),因此我们决定仅使用已检查的异常.

在我目前的工作中,我发现在很多或所有情况下都有很多人会遇到运行时异常.

这就是我的想法:使用CheckedExceptions,我在编译时被迫至少在调用者中确认异常.对于运行时异常,我不会被编译器强迫,但可以编写一个单元测试,让我处理它.因为我仍然认为越早发现一个bug就越便宜,我就更喜欢CheckedExceptions.

从哲学的角度来看,方法调用是调用者和被调用者之间某种程度的契约.由于编译器强制执行传入的参数类型,因此它似乎是对称的,以便在出路时强制执行类型.也就是说,返回值或异常.

我的经验告诉我,当我使用经过检查的异常时,我获得了更高的质量,也就是代码.检查异常可能会使代码混乱,但有一些技术可以解决这个问题.我喜欢在传递图层边界时翻译异常.例如,如果我从持久层传递,我想将SQL异常转换为持久性异常,因为下一层不应该关心我是否持久化到SQL数据库,但是想要知道是否有东西不能坚持下去.我使用的另一种技术是创建一个简单的异常层次结构.这让我可以编写一层更清晰的代码,因为我可以捕获超类,并且只在真正重要时处理各个子类.


"结束你的代码污染与大量的空/重新捕获块" - 如果你看到这一点,他们没有被正确使用.你可以采取任何技术,使用错误,并提出批评.
就单元测试而言,我同意代码应该进行单元测试.TDD,实际上.但要记住费用的连续性.同样的连续性使得单元测试中的错误检测比生产中更便宜,使得编译器检测到的错误比UT捕获的错误便宜.

2> Jonik..:

总的来说,我认为Joshua Bloch在Effective Java中的建议最好地总结了您的问题的答案:使用已检查的可恢复条件和编程错误的运行时异常(第2版中的第58项).

所以在这种情况下,如果你真的想要使用异常,它应该是一个经过检查的异常.(除非文档transferTo()清楚地表明,如果首先使用其他Account方法检查足够的平衡,就不能调用该方法 - 但这看起来有点尴尬.)

但另请注意项目59:避免不必要地使用已检查的例外和57:仅在例外条件下使用例外.正如其他人所指出的那样,这个案件可能根本不需要例外.false如果信用不足,请考虑返回(或者可能是状态对象,其中包含有关所发生事件的详细信息).


我认为在Java中应该没有返回"boolean"的方法来指示成功或失败.机会太高,调用者可能忘记检查返回的值.在这种情况下抛出异常似乎更合适,然后此异常可能会带有您建议的状态对象.
@Roland:我不同意你的一揽子断言,但不同意你的原则.:-)这与Jonik的Bloch引用没有什么不同:在异常条件下使用例外.如果该方法在正常操作中可能合理地"失败"(例如Collection.add),那么成功返回是合理的.如果失败是意外编码(例如保存到文件)的结果,则异常是合适的.糟糕的开发人员是否可以自己投篮,这是一个微不足道的问题,因为他们会找到一种方法.

3> cletus..:

何时使用已检查的例外?说实话?以我的拙见......从不.我认为自从我上次创建一个检查异常以来已经过去了大约6年.

你不能强迫某人处理错误.可以说,它使代码变得更糟,而不是更好.我不能告诉你我遇到这样的代码的次数:

try {
  ...
} catch (IOException e) {
  // do nothing
}

虽然我有无数次编写这样的代码:

try {
  ...
} catch (IOException e) {
  throw new RuntimeExceptione(e);
}

为什么?因为一个条件(不一定是IOException;这只是一个例子)是不可恢复的,但是无论如何都被迫放下我的喉咙而且我经常被迫在执行上述操作和污染我的API之间做出选择,只是为了传播一个检查过的异常到顶部它(正确地)致命并将被记录.

Spring的DAO帮助程序类将检查的SQLException转换为未经检查的DataAccessException是有原因的.

如果您对磁盘缺少写入权限,缺少磁盘空间或其他致命条件,您希望尽可能多地产生噪声,并且执行此操作的方法是使用...未经检查的异常(甚至是错误).

此外,已检查的异常会破坏封装.

检查异常的这个想法应该被用于"可恢复的"错误,这实际上是一种天空般的一厢情愿的想法.

Java中检查的异常是一个实验......一个失败的实验.我们应该减少损失,承认我们犯了错误并继续前进.恕我直言.Net通过仅具有未经检查的异常使其正确.然后,它再次获得了从Java的错误中学习的第二个优势.


cletus - 有关"例外打破封装"链接的有趣之处在于,如果您使用经过检查的例外**,他的大多数异议(包括同名异议)都会失效**.抛出异常*如果被检查则不可见*.如果方法声明它将其异常作为其合同的一部分抛出,则异常*不会破坏封装.如果开发人员无法被信任正确处理已检查的异常,那么在编译器的帮助下,为什么他们会被要求正确处理未经检查的异常?
从根本上说,已检查异常和未经检查的异常之间的唯一区别是编译器**强制**您记录已检查的异常.您应该记录代码可以抛出的任何异常*无论如何*; 我不明白为什么有人会反对这种静态可检查,以防止它陈旧或任何例外被遗漏.无论是否检查,例外*都是您的API的一部分; 检查只是确保您准确地声明您的API.
未经检查的异常会使您的抽象泄漏...如果您拥有它们.如果您的设计相对不稳定,并且需要更具体的代码,那么未经检查的异常就可以了.在系统的稳定的抽象层中,经过检查的异常保留了抽象.
我同意这一点; 我已经在广泛的代码库上使用了运行时异常(大约2000个类在库和应用程序之间平均分配),并且在生产中没有未处理的异常时遇到实际问题.
未经检查的异常与检查的异常相比没有或多或少泄漏.声明代码抛出IOException或SQLException会说明您的实现,因此您可以拥有一个特殊的已检查异常,但您也可以拥有一个特殊的未经检查的异常.这没什么不同.
如果服务层声明了UserAlreadyExistsException,则客户端可以捕获并恢复。如果数据访问层只是让未经检查的Hibernate ConstraintViolationExceptions冒泡,那么任何想要从该特定问题中恢复的调用者都将依赖于特定实现的编译时依赖。受检查的异常鼓励抽象的实现将低级异常重写为适合抽象的形式,从而防止实现泄漏。

4> Henrik Paul..:

恕我直言,它应该不是一个例外.在我看来,应该在特殊情况发生时使用异常,而不是作为流控制.

在你的情况下,有人试图转移比余额允许更多的钱,这并不是一个特殊的状态.我认为这些事情在现实世界中经常发生.所以你应该针对这些情况进行编程.一个例外可能是你的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();
}
// ...



5> IAdapter..:

我最近遇到了异常问题,代码抛出了NullPointerException,我不知道为什么,经过一些调查后发现真正的异常被吞噬了(它是在新代码中,所以它仍在完成)并且方法只返回n​​ull.如果你检查了异常,你必须明白坏程序员只会尝试捕获它并忽略异常.



6> kazanaki..:

我的规则是

if语句的业务逻辑错误(如代码)

应用程序可以恢复的环境错误的异常

没有恢复的环境错误的未经检查的异常

    已检查例外的示例:对于可脱机工作的应用程序,网络已关闭

    未经检查的异常的示例:数据库在CRUD Web应用程序上关闭.

有很多关于这个主题的文档.您可以通过浏览Hibernate网页找到很多,因为他们在版本3.x中将Hibernate 2.x的所有异常从已检查更改为未选中


可恢复性是呼叫者的"眼睛"; 即,实现者没有业务假设客户能够或不能从中恢复.惯例是编程错误导致运行时异常,而其余的则被检查.

7> Derek Mahar..:

从未经检查的例外 - 争议:

如果可以合理地期望客户端从异常中恢复,则将其作为已检查的异常.如果客户端无法执行任何操作以从异常中恢复,请将其设置为未经检查的异常.

请注意,未经检查的异常是派生自RuntimeException的异常,并且已检查的异常是派生自的异常Exception.

RuntimeException如果客户端无法从异常中恢复,为什么抛出?文章解释说:

运行时异常表示编程问题导致的问题,因此,无法合理地期望API客户端代码从它们恢复或以任何方式处理它们.这些问题包括算术异常,例如除以零; 指针异常,例如尝试通过空引用访问对象; 和索引异常,例如尝试通过索引太大或太小来访问数组元素.


不幸的是,在许多情况下,*某些*客户端有望从异常中恢复,但许多情况不会。此外,在许多情况下,方法抛出异常的可能性将取决于传递给它的参数。在很多情况下都不会抛出“ checked”异常,这迫使调用者添加代码,这些代码只能在某些严重错误而无法进行有意义的恢复的情况下执行。
`try {URL u =新的URL(“ http://example.com/”);} catch(MalformedURLException e){/ *由于URL是静态的,_never_可以执行,但无论如何我们必须对其进行处理* /}`。这样可以防止测试上的代码覆盖率达到100%,并增加了毫无意义的混乱。URL无法知道将如何使用它,因此实现有时会做出错误的假设。那怎么是个好主意?

8> Bob Cross..:

我的感觉是,经过检查的例外是一个有用的合同,应该谨慎使用.我认为检查异常是一个好主意的经典示例是InterruptedException.我的感觉是,我希望能够在我希望它停止时停止线程/进程,无论有人指定Thread.sleep()多长时间.

所以,试图回答你的具体问题,这是你绝对想要确保每个人都处理的问题吗?在我看来,一个Account没有足够资金的情况是一个严重的问题,你必须处理它.

回应Peter的评论:这是一个使用InterruptedException作为应该处理的异常的具体情况的例子,你需要有一个有用的默认处理程序.这是我强烈推荐的,当然是在我的实际工作中.你应该至少这样做:

catch (InterruptedException ie) {
    Thread.currentThread().interrupt();
}

该处理程序将确保代码捕获已检查的异常并完全按照您的要求执行:使此线程停止.不可否认,如果上游还有另一个异常处理程序/食者,那么它将不太可能处理异常.即便如此,FindBugs可以帮助您找到这些.

现在,现实开始了:你不一定强迫每个为你的检查异常编写异常处理程序的人都能很好地处理它.也就是说,至少你可以"查找用法"并知道它的用途并给出一些建议.

简短形式:如果使用已检查的异常,则会对方法的用户造成负担.确保有充分的理由,建议正确的处理方法,并广泛记录所有这些.


怎么可能更好?如果你在没有超时的情况下处于get()之类的中间并且你收到InterruptedException,那么你应该打断该线程.已检查的异常是一个尖锐的提示,这是一个必须处理的特殊且重要的案例.
推荐阅读
手机用户2402852307
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有