在Java中,是使用throws Exception而不是抛出多个特定exception的好习惯?

在查看Spring MVC框架时,我注意到,除非我误解,否则它的开发人员会抛出抛出Exception而不是抛出多个exception。

我意识到这个问题的核心是检查与未检查的exception辩论,避免宗教战争,使用抛出一般exception是一种好的做法吗?

对于像Spring MVC这样需要开放以适应各种不同用例的库而言,有意义的是,在编写特定应用程序时,您不一定有意义。 这是其中一个案例。

如果您指的是诸如Controller接口之类的类作为方法签名,例如

 handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception 

这可能是因为,从调用Controller的Spring类(如DispatcherServlet )的角度来看,它们并不关心代码调用的Exception类型 – 像DispatcherServlet这样的库代码只需要知道这个类可能会抛出exception,因此能够在一般情况下处理exception。

换句话说, DispatcherServlet不需要知道控制器可能抛出的Exception的特定类型 – 它会将它们中的任何一个视为“错误”。 这就是方法签名throws Exception

现在,API作者可以使签名使用自定义exception类型作为SpringMvcException ,但这只会强制您在handleRequest方法中处理任何已检查的exception类型并简单地包装它们,这是繁琐的make-work样板码。 因此,由于Spring的所有内容都旨在使其尽可能轻松和轻量级地集成,因此它们更容易指定接口方法仅throws Exception

不,绝对不是。 您应该指定要抛出的exception,以便调用者可以对每个exception执行正确的操作。 如果你不这样做,那么“throws Exception”会被链接传递,调用者可以做的最好的事情就是printStackTrace()并死掉。

更新:为了对抗一些“如果我覆盖方法”的反对意见,我会更进一步说,只要你有一个抛出exception的包(而不是从调用者传递exception),你应该在该包中声明一个exception类。 因此,如果你覆盖我的“addToSchedule()抛出ScheduleConflictException”,你完全能够将ScheduleConflictException子类化为你需要的东西。

这是抛出特定exception的问题…假设有人扩展了您的类并希望覆盖您的方法。 假设他们的新实现需要抛出不同类型的exception。 (你怎么能预测一个重写方法可能需要抛出的exception?)编写覆盖方法的人只有两个选择:1)自己处理exception(可能是一个糟糕的选择),或2)包装真实其中一个允许的exception类型中的exception并重新抛出。

但是选项2有两个问题。 首先,当您将exception转储到日志文件时,您将获得长期丑陋的嵌套exception链。 更重要的是,您将失去捕获特定exception的能力。 例如,假设重写方法调用另一个与数据库对话的方法,如果生成的SQL导致死锁,则抛出DeadlockException。 重写方法必须捕获此exception,将其包装在允许的类型之一中,然后重新抛出。 这使得堆栈中的代码无法捕获并检测DeadlockException。

您的问题最终会进入关于已检查与未检查exception的争论的核心。 你可以在Google上找到辩论双方的许多论据。 我认为最终,如果您相信已检查的exception,您应该非常清楚方法抛出的exception。 如果您不喜欢已检查的exception,则应声明每个方法都抛出exception。 我落入后一阵营。

顺便说一句,对于那些不喜欢检查exception的人,我不喜欢在任何地方使用RuntimeException的想法。 问题是您可能需要合并使用Exception而不是RuntimeException的第三方库。 然后,您的代码必须从库中捕获所有Exception并将它们包装在RuntimeException中。 这造成了一团糟。

所以,如果我再次从头开始Java项目,我只是声明每个方法都抛出exception。

没有。

抛出exception强制调用代码来捕获exception,由于各种原因,它们可能不希望这样做。

恕我直言,所有的设计讨论和建议都说明了一般应该做些什么,他们有一个支持他们的原则。 但通常这些建议并不适用于所有情况。

在exception处理的情况下,使用已检查exception的想法是,通常以不同方式捕获和管理exception通常是好的,具体取决于您的需要。 但是,如果你确定它们都会以同样的方式被捕获和管理,那么一直检查类型是没有意义的。

IMO不抛出exception的最大原因是,如果他们真正有兴趣采取某些行动,它会迫使我们的客户catch(Exception e)

catch(Exception e)在于,Exception在检查时也是RuntimeException的超类,因此强制客户端也捕获所有RuntimeExceptions

可悲的是,没有捕获(CheckedException e)技术。 假设你的穷人客户端不需要捕获RuntimeExceptions,他们必须执行一个instanceof检查并重新抛出它的RuntimeException。

在我看来是的,因为这允许你抛出你想要的任何exception。

随着程序的增长,您可能需要抛出与您原先想到的不同的exception。 如果定义了每个单独的exception类型,则必须修改方法签名,然后修改调用它的所有方法以正确处理新exception。

只使用’throws Exception’允许任何调用方法的方法正确处理你列出的任何方法,但每当你向函数中添加新的exception时,它都不会破坏这些其他方法。 (虽然您应该更新它们以处理新的exception,但这不是必需的。)

您需要区分通用代码和更具体的代码。

将函数的返回类型视为类比。 如果您正在编写类似“ArrayList”的类,那么您希望它非常通用,因此参数和返回值通常是通用的“对象”。 但是当你编写更具体的应用程序代码时,比如“getRetiredEmployees”函数,返回Object将是一个非常糟糕的主意。 您更可能想要返回Employee []或类似的东西。

可以肯定的是,像Spring这样的框架会期望genericsexception,因为它不知道你的特定应用程序会抛出什么exception。 Spring的作者无法知道您将抛出“EmployeeNotInSpecifiedDepartment”exception或其他任何内容。 他们怎么样? 但是如果您正在编写sendPensionChecks函数并调用getRetiredEmployees,那么您可以合理地期望知道该函数可能抛出的特定exception,以及您应该如何处理它们。

Clint Miller提出了一个关于不知道如何扩展课程的有效观点。 我承认这是使您的例外具体化的问题。 但是从那里开始“只是让一切都成为一般的例外”就太容易放弃了。 这就像是说,因为有一天某人可能会扩展我们的getRetiredEmployees函数,在某些情况下会返回EmployeeSpouse和Employee,因此我们应该放弃并返回类型为Object。 如果这是您在内部使用并且您可以控制的代码,那么这是一个无问题:如果您需要向函数添加新的exception,则添加它。 如果这是您要向全世界发布的API,那么是的,问题就更棘手了。 我要说的是,一般的解决方案是尝试合理地思考哪些例外是有意义的,并将它们全部包括在内,即使它们目前并非全部实施。 如果你的一个客户正在做一些完全无法预料的事情,那么,这是一个没有简单答案的问题。

使一切都通用不是正确的答案,因为它使一切难以理解,难以维护,并且难以validation准确性。

我实际上从编译器中删除了检查的exception。

这比人们猜测的更好。

现在,很大一部分配置错误显示为未处理的exception,但读取第一行文本会使真正的问题变得明显。

我一直在计划修改顶级exception处理程序以显示错误消息框,而不是几个exception类型(主要是IOException)的未处理exception框。 我已经检查了一个ErrorMessageexception。

Brian Goetz在这里谈到了其中的一些问题。 另请参阅Josh Bloch撰写的Effective Java 2nd Edition。 他专门讨论了例外情况。 此外,Spring Framework的创始人Rod Johnson详细介绍了他对J2EE设计和开发中 Javaexception处理的看法。 你可能会也可能不同意他的exception处理哲学,但至少可能更有意义的是他们为什么做出他们所做的决定。

alt text http://java.sun.com/docs/books/images/effective.jpg 替代文字

列出每个例外。 这条路:
*它清楚地定义了什么可能出错。
*您class级的用户可以完全了解他们应该处理的例外情况。

此外,一个重要的方法应该做同样的事情,因为被覆盖,但以不同的方式。 因此,你怎么能有另一个exception,它不是已经抛出的exception的子类? 你不应该。 此外,你不应该在子类中抛出新的exception,因为子类应该能够替换它的父类,因此如果它被传递为“p”到

 public void method(Parent p); 

“方法”如何知道它必须处理一些新的exception? 它不应该。 它意味着不正确的设计(父类或子类)。

一般来说,“抛出exception”很少是一个好主意。

在运行计算机代码时,运行时错误大致可分为两大类。 有些是错误代码的结果 – 可能是被调用方法本身的逻辑错误,或者传递给它的错误输入。 然后有些问题是由于程序员无法控制的情况造成的。 也许系统正在尝试打开另一个进程刚刚删除的文件,或者通过刚刚关闭的网络访问远程服务器。

如果发生第一种exception,您的程序没有理由抓住并继续。 它应该干净利落地显而易见,明确发生了什么,以便你可以得到警告,解决出错的问题。

第二种例外是您想要捕获的那种。 您不知道在任何给定的运行中是否会出现这些问题,但您可以很好地了解代码中可能出现的WHERE – 基本上,只要您需要依赖程序控制之外的资源。

声明抛出的exception本质上是一个方法的作者和使用它的客户之间的契约,告诉你你应该期望什么可能出错。 毯子“抛出exception”相当于抛弃你的手并说客户必须准备好任何错误,甚至在早期调试中已经找到并修复的问题。 呼叫者必须要么抓住这些例外 – 并且通常只是吃掉它们并继续,因为合同范围很广,以至于他们不知道问题出在哪里 – 或者将它们放在上面,迫使其他人处理相同的问题。

是的,不是。 是的,只是抛出或捕获一个exception并且完成它是“方便的”,但是在任何尝试从exception中优雅地恢复它确实会伤害你。 某些例外可以而且应该优雅地处理,这样你的程序就不会死于可怕的死亡。 如果一个方法只是抛出exception,我不知道出了什么问题,所以我没有任何希望修复并从问题中恢复。

一个可能的错误和解决方案包括

解析整数时 – NumberFormatException 。 我们可以有一个默认值

最重要的是,我认为保持个别例外是一种更好的做法。

抛出exception也会导致J2EE中的事务管理出现大量问题。 使用catchexception是纯粹的邪恶!

我没有给你奖励小猫!

棘手。

理想情况下,指定exception构成合同的一部分,告诉用户可能会出现什么exception。 这有助于他们决定要处理什么,什么不处理。

在编写框架或库时虽然无法说 –

abstract T func(…)抛出A,B,C;

因为任何扩展func(…)容器的类都可能会覆盖func。

我自己不得不使用’抛出exception’的宽松规范,但作为经验法则

  • 为私有方法提供严格的exception规范(在本讨论的上下文中,我认为方法在它们的契约是不可变的意义上是私有的),对所有其他方法都是松散的规范。