When to choose checked and unchecked exceptions在Java(或任何其他具有已检查异常的语言)中,在创建自己的异常类时,如何确定是应该选中还是取消选中它? 我的直觉是,如果调用者能够以某种富有成效的方式恢复,那么将调用一个已检查的异常,其中未经检查的异常对于不可恢复的情况更多,但我会对其他人的想法感兴趣。 只要您了解应该使用它们,检查的例外情况就很好。 Java核心API无法遵循SQLException的这些规则(有时候对于IOException),这就是它们如此糟糕的原因。 Checked Exceptions应该用于可预测但不可避免的错误,这些错误可以从中恢复。 未经检查的例外应该用于其他一切。 我会为你打破这个,因为大多数人误解了这意味着什么。 除非您抛出的异常符合上述所有条件,否则应使用未经检查的异常。 在每个级别重新评估:有时捕获已检查异常的方法不是处理错误的正确位置。在这种情况下,请考虑对您自己的呼叫者来说什么是合理的。如果异常是可预测的,不可避免且合理的,他们可以从那时恢复,你应该自己抛出一个检查异常。如果不是,则应将异常包装在未经检查的异常中。如果您遵循此规则,您将发现自己将已检查的异常转换为未经检查的异常,反之亦然,具体取决于您所在的层。
对于已检查和未检查的异常,请使用正确的抽象级别。例如,具有两个不同实现(数据库和文件系统)的代码存储库应避免通过抛出 来自Java学习者:
相反的情况非常强烈:绝不使用已检查的异常。我不愿在辩论中偏袒任何一方,但似乎有一个广泛的共识,即在事后看来,引入经过检查的例外是一个错误的决定。请不要拍摄信使并参考这些论点。 在具有多个层的任何足够大的系统上,检查异常都是无用的,无论如何,您需要一个架构级策略来处理异常的处理方式(使用故障屏障) 使用已检查的异常,您的错误处理策略是微管理的,并且在任何大型系统上都无法忍受。 大多数情况下,您不知道错误是否"可恢复",因为您不知道API的调用者位于哪个层中。 假设我创建了一个StringToInt API,它将整数的字符串表示形式转换为Int。如果使用"foo"字符串调用API,我必须抛出一个已检查的异常吗?它可以恢复吗?我不知道,因为在他的图层中,我的StringToInt API的调用者可能已经验证了输入,并且如果抛出此异常,则可能是错误或数据损坏,并且此层无法恢复。 在这种情况下,API的调用者不希望捕获异常。他只想让例外"冒泡"。如果我选择了一个已检查的异常,则此调用者将有大量无用的catch块仅用于人为地重新抛出异常。 可恢复的大部分时间取决于API的调用者,而不是API的写入者。 API不应使用已检查的异常,因为只有未经检查的异常允许选择捕获或忽略异常。 你说的没错。 未经检查的异常用于让系统快速失败,这是一件好事。您应该清楚地说明您的方法是什么,以便正常工作。这样,您只能验证输入一次。 例如:
只是举一个例子。关键是,如果系统快速失败,那么你就会知道它失败的地点和原因。你会得到一个堆栈跟踪:
你会知道发生了什么。"delegateTheWork"方法中的OtherClass(在第4569行)使用"exit"值调用您的类,即使它不应该等。 否则,您将不得不在代码中进行验证,这很容易出错。此外,有时很难追踪出现问题的原因,您可能会遇到数小时令人沮丧的调试 NullPointerExceptions也会发生同样的事情。如果你有一个包含大约15个方法的700行类,它们使用30个属性,并且它们都不能为null,而不是在每个这些方法中验证可空性,你可以将所有这些属性设置为只读并在构造函数中验证它们或工厂方法。
检查异常当程序员(您或您的同事)做的一切正确,验证输入,运行测试以及所有代码都很完美,但代码连接到可能已关闭的第三方Web服务(或文件)时非常有用你使用的是被另一个外部进程删除等)。 Web服务甚至可以在尝试连接之前进行验证,但在数据传输期间出现问题。 在那种情况下,您或您的同事无法帮助它。但是你仍然需要做一些事情而不是让应用程序死掉并消失在用户眼中。您使用已检查的异常并处理异常,当发生这种情况时您能做什么?大多数情况下,只是为了尝试记录错误,可能会保存您的工作(应用程序工作)并向用户显示一条消息。 (该网站blabla已关闭,请稍后重试等) 如果检查过的异常被过度使用(通过在所有方法签名中添加"throw Exception"),那么你的代码将变得非常脆弱,因为每个人都会忽略该异常(因为太笼统)并且代码质量会严重损害。 如果过度使用未经检查的异常,则会发生类似情况。该代码的用户不知道是否会出现问题,会出现很多try {...} catch(Throwable t)。
这是我的"最终经验法则"。
与之前的答案相比,这是使用一种或另一种(或两种)例外的明确理由(人们可以同意或不同意)。 对于这两个异常,我将为我的应用程序创建自己的未经检查和检查的异常(这是一个很好的实践,如此处所提到的),除了非常常见的未经检查的异常(如NullPointerException)
因此,例如,下面这个特定函数的目标是使(或者如果已经存在)一个对象,
例:
这不仅仅是从异常中恢复的能力问题。在我看来,最重要的是调用者是否有兴趣捕获异常。 如果您编写要在其他地方使用的库或应用程序中的较低级别层,请问自己调用者是否有兴趣捕获(了解)您的异常。如果他不是,那么使用未经检查的例外,这样你就不会给他带来不必要的负担。 这是许多框架使用的哲学。特别是,我想到了Spring和hibernate - 它们将已知的已检查异常转换为未经检查的异常,因为在Java中过度使用了已检查的异常。我能想到的一个例子是来自json.org的JSONException,它是一个经过检查的异常,并且很烦人 - 它应该是未经检查的,但开发人员根本没有想到它。 顺便说一句,大多数情况下,调用者对异常的兴趣与从异常中恢复的能力直接相关,但情况并非总是如此。 对于您的Checked / Unchecked困境,这是一个非常简单的解决方案。
规则1:在代码执行之前将未经检查的异常视为可测试条件。
其中x为null ...
规则2:将Checked Exception视为代码执行时可能出现的不可测试条件。
...在上面的示例中,由于DNS服务器关闭,URL(google.com)可能无法使用。即使在DNS服务器正在工作并且将"google.com"名称解析为IP地址的情况下,如果连接到google.com,则在任何时候后续,网络可能会关闭。在读取和写入流之前,您根本无法一直测试网络。 在我们知道是否存在问题之前,有时候代码必须执行。通过强制开发人员以强制他们通过Checked Exception处理这些情况的方式编写代码,我不得不向发明这个概念的Java的创建者倾斜。 通常,Java中的几乎所有API都遵循上面的2条规则。如果您尝试写入文件,则磁盘可能会在完成写入之前填满。其他进程可能导致磁盘已满。没有办法测试这种情况。对于那些随时与硬件交互的人来说,使用硬件可能会失败,Checked Exceptions似乎是解决这个问题的优雅方案。 这有灰色区域。如果需要进行许多测试(一个令人兴奋的if语句带有大量的&&和||),抛出的异常将是一个CheckedException,因为它过于痛苦才能正确 - 你根本就不能说这个问题是一个编程错误。如果少于10个测试(例如'if(x == null)'),那么程序员错误应该是UncheckedException。 处理语言翻译时,事情变得有趣。根据上述规则,语法错误是否应被视为已检查或未检查的异常?我认为如果语言的语法可以在执行之前进行测试,那么它应该是一个UncheckedException。如果无法测试语言 - 类似于汇编代码在个人计算机上运行的方式,那么语法错误应该是一个Checked Exception。
上述2条规则可能会消除90%的您可以选择的问题。要总结规则,请遵循以下模式...... 您可以将其称为已检查或未检查的异常;但是,程序员可以捕获这两种类型的异常,因此最好的答案是:将所有异常写为未选中并记录它们。这样,使用您的API的开发人员可以选择是否要捕获该异常并执行某些操作。检查异常完全浪费了每个人的时间,这使得您的代码成为令人震惊的噩梦。然后,适当的单元测试将显示您可能必须捕获并执行某些操作的任何异常。
检查异常:
未经检查的例外情况: 示例:如果您希望在方法A()中进行算术运算并基于A()的输出,则必须执行另一个操作。如果输出在方法A()中为null,而在运行时期间您没有预料到,那么您应该抛出Null指针Exception,即运行时异常。 请参考这里 我同意对未经检查的异常的偏好,特别是在设计API时。调用者总是可以选择捕获记录的,未经检查的异常。你不是不必要地强迫来电者。 我发现在较低级别有用的检查异常,作为实现细节。通常看起来比控制指定错误"返回代码"更好的控制机制流程。它有时可以帮助看到一个想法对低级别代码更改的影响...在下游声明一个已检查的异常并查看谁需要调整。如果有很多泛型:catch(异常e)或抛出异常,那么最后一点不适用,这通常不会过于深思熟虑。 以下是我想分享我多年的开发经验后的看法:
检查异常。这是业务用例或调用流程的一部分,这是我们期望或不期望的应用程序逻辑的一部分。例如连接被拒绝,条件不满足等。我们需要处理它并向用户显示相应的消息,说明发生了什么以及下一步做什么(稍后再试)等。
未经检查的异常。这是编程异常的一部分,软件代码编程中的一些错误(错误,缺陷),反映了程序员必须按照文档使用API??的方式。如果外部lib / framework文档说它希望获得某个范围内的数据而非null,因为将抛出NPE或IllegalArgumentException,程序员应该期望它并按照文档正确使用API??。否则将抛出异常。 目标受众。现在让我们谈谈目标受众或一组例外的人(根据我的观点): 按应用程序开发生命周期阶段。 框架通常使用未经检查的异常(例如Spring)的原因是框架无法确定应用程序的业务逻辑,这取决于开发人员是否能够捕获并设计自己的逻辑。 我们必须根据它是否是程序员错误来区分这两种类型的异常。
FileNotFoundException是了解细微差别的好例子。如果找不到文件,则抛出FileNotFoundException。这种例外有两个原因。如果文件路径由开发人员定义或通过GUI从最终用户获取,则应该是未经检查的异常。如果该文件被其他人删除,则应该是Checked Exception。 Checked Exception可以通过两种方式处理。这些是使用try-catch或传播异常。在传播异常的情况下,由于异常处理,调用堆栈中的所有方法都将紧密耦合。这就是为什么我们必须仔细使用Checked Exception。 如果您开发了一个分层的企业系统,您必须选择大多数未经检查的异常才能抛出,但不要忘记在您无法执行任何操作的情况下使用已检查的异常。 每当异常不太可能发生时,我们甚至可以在捕获之后继续进行,并且我们无法做任何事情来避免该异常,那么我们可以使用checked异常。 每当我们想要在特定异常发生时做一些有意义的事情,并且当预期异常但不确定时,我们就可以使用检查异常。 每当异常在不同层中导航时,我们不需要在每个层中捕获它,在这种情况下,我们可以使用运行时异常或将异常包装为未经检查的异常。 当最有可能发生的异常时,使用运行时异常,没有办法进一步发展,没有任何东西可以恢复。因此,在这种情况下,我们可以针对该例外采取预防措施。 EX:NUllPointerException,ArrayOutofBoundsException。这些更有可能发生。在这种情况下,我们可以在编码时采取预防措施以避免此类异常。否则我们将不得不在每个地方编写try catch块。 可以进行更多常规例外未选中,检查不太通用。 我想我们可以从几个问题考虑一些问题: 为什么会出现这种情况?当它发生时我们能做什么 错误的,一个错误。例如调用null对象的方法。
在测试期间应该修复这种异常。否则,它会破坏生产,你得到一个非常高的bug,需要立即修复。不需要检查这种异常。 通过外部输入,您无法控制或信任外部服务的输出。
在这里,如果要在null为空时继续,可能需要检查名称是否为空,否则,您可以单独使用它,它将在此处停止并为调用者提供运行时异常。 通过来自外部的运行时异常,您无法控制或信任外部服务。 在这里,你可能需要捕获来自ExternalService的所有异常,如果你想在它发生时继续,否则,你可以让它独自一个,它将停在这里并给调用者一个运行时异常。 通过从外部检查异常,您无法控制或信任外部服务。 在这里,你可能需要捕获来自ExternalService的所有异常,如果你想在它发生时继续,否则,你可以让它独自一个,它将停在这里并给调用者一个运行时异常。 在这种情况下,我们是否需要知道ExternalService中发生了什么样的异常?这取决于: 如果你能处理某些异常,你需要捕获它们并进行处理。对于其他人,泡他们。 如果您需要记录或响应用户特定的execption,您可以捕获它们。对于其他人,泡他们。 对于要向调用者提供信息的可恢复情况(即权限不足,文件未找到等),已检查的异常非常有用。 如果有的话,未经检查的异常很少用于在运行时通知用户或程序员严重错误或意外情况。如果您正在编写将由其他人使用的代码或库,请不要抛弃它们,因为它们可能不会期望您的软件抛出未经检查的异常,因为编译器不会强制它们被捕获或声明。
我认为在声明Application Exception时应该是Unchecked Exception,即RuntimeException的子类。 我使用的规则是:永远不要使用未经检查的异常! (或当你看不到任何方式时) 从开发人员使用您的库或最终用户使用您的库/应用程序的角度来看,真的很难面对因未知异常导致崩溃的应用程序。依靠抓住机会也无济于事。 这样,最终用户仍然可以看到错误消息,而不是应用程序完全消失。 |