在Java中,是使用throws Exception而不是抛出多个特定异常的良好实践?

Jam*_*hon 14 java exception-handling exception

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

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

Pau*_*lin 18

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

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

  • 在非平凡大小的应用程序中,没有*正确的方法来使用已检查的异常.这是一个根本上有缺陷的概念,因为它强制方法的直接客户端处理异常,该异常直接针对异常的概念运行 - 允许开发人员在调用堆栈中的适当点处理它们. (5认同)
  • 在大多数情况下,这是调用者最终做的事情,并且用几十个异常签名(或包装异常)乱丢签名并不比仅仅声明"抛出异常"更好.是的,我完全在那些认为检查异常是完全失败的实验的人的阵营中. (2认同)
  • @Michael Borgwardt,我不知道大多数开发人员滥用像检查异常这样的东西是否真的是一个有效的论据,反对检查异常.让我们解决这些白痴开发者,而不是他们不理解的语言概念. (2认同)

mat*_*t b 16

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

如果你指的是诸如Controller接口等类作为方法签名之类的

handleRequest(HttpServletRequest request, HttpServletResponse response) 
   throws Exception
Run Code Online (Sandbox Code Playgroud)

这可能是因为,从调用Controller的Spring类的角度来看DispatcherServlet,它们并不关心你的代码调用什么类型的Exception - 库代码DispatcherServlet只需要知道这个类可能会抛出异常因此能够在一般情况下处理异常.

换句话说,DispatcherServlet不需要知道控制器可能抛出的异常的特定类型 - 它将把它们中的任何一个视为"错误".这就是方法签名的原因throws Exception.

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


Cli*_*ler 7

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

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

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

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

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