在Java中,使用抛出异常而不是抛出多个特定异常是一种好的做法吗?

在Java中,使用抛出异常而不是抛出多个特定异常是一种好的做法吗?,java,exception,exception-handling,Java,Exception,Exception Handling,在浏览SpringMVC框架时,我注意到,除非我误解,否则它的开发人员更喜欢抛出异常,而不是抛出多个异常 我意识到这个问题的核心是检查与未检查异常的争论,避免了宗教战争,使用抛出通用异常是一种好的做法吗?在我看来是的,因为这允许你抛出任何你想要的异常 随着程序的增长,您可能需要抛出一个不同于您最初设想的异常。如果定义了每个单独的异常类型,则必须修改方法签名,然后修改调用它的所有方法以正确处理新异常 仅使用“throws Exception”就允许任何调用您的方法的方法正确处理您列出的任何方法,但

在浏览SpringMVC框架时,我注意到,除非我误解,否则它的开发人员更喜欢抛出异常,而不是抛出多个异常


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

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

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


仅使用“throws Exception”就允许任何调用您的方法的方法正确处理您列出的任何方法,但无论何时向函数中添加新的异常,它都不会破坏这些其他方法。(虽然您可能应该更新它们以处理新的异常,但这不是必需的。)

不,绝对不是。您应该指定要抛出的异常,以便调用方可以对每个异常执行正确的操作。如果不这样做,“抛出异常”将被传递到链的上游,调用方所能做的最好的事情就是printStackTrace()并死亡

更新:为了反驳一些“如果我重写该方法会怎么样”的反对意见,我想进一步说,每当您有一个抛出异常的包(而不是从调用方传递异常),您都应该在该包中声明一个异常类。因此,如果您要覆盖我的“addToSchedule()抛出ScheduleConflictException”,那么您完全可以将ScheduleConflictException子类化以完成您需要的任务。


抛出异常会迫使调用代码捕获异常,而出于各种原因,它们可能不想这样做。

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


在异常处理的情况下,使用检查异常的想法是,根据您的需要,以不同的方式捕获和管理异常通常是很好的。但是,如果您确信它们都将以相同的方式被捕获和管理,那么一直检查类型是没有意义的。

下面是引发特定异常的问题。。。假设有人扩展了您的类并希望重写您的方法。假设他们的新实现需要抛出不同类型的异常。(您如何预测重写方法可能需要抛出哪些异常?)编写重写方法的人只有两个选择:1)自己处理异常(可能是一个错误的选择),或2)将真正的异常封装在允许的异常类型中,然后重新抛出

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

您的问题最终进入了关于检查与未检查异常的辩论的核心。你可以在谷歌上搜索并找到辩论双方的许多论据。我认为,最终,如果您相信检查异常,那么应该非常明确地说明方法抛出的异常。若您不喜欢检查异常,那个么应该声明每个抛出异常的方法。我属于后一个阵营

顺便说一下,对于不喜欢检查异常的人,我不喜欢到处使用RuntimeException的想法。问题是您可能需要合并一个使用Exception而不是RuntimeException的第三方库。然后,您的代码必须捕获库中的所有异常,并将它们包装到RuntimeException中。这会造成混乱


因此,如果我再次从头开始一个Java项目,我只需要声明每个抛出异常的方法。

对于像Spring MVC这样的库来说,有意义的是什么,它需要足够开放以适应各种不同的用例,在编写特定的应用程序时,不一定要遵循它。这就是其中之一

如果您指的是类,例如
控制器
接口,它作为方法签名,例如

handleRequest(HttpServletRequest request, HttpServletResponse response) 
   throws Exception
这可能是因为,从调用控制器的Spring类(例如
DispatcherServlet
)的角度来看,他们不关心您的代码调用什么类型的异常-库代码(如
DispatcherServlet
)只需要知道此类可能引发异常,因此在一般情况下能够处理异常

换句话说,
DispatcherServlet
不需要知道您的控制器可能引发的特定类型的异常-它会将其中任何一种视为“错误”。这就是为什么方法签名是
抛出异常的原因

现在,API作者可以让签名使用自定义异常类型,如
SpringMvcException
,但这只会迫使您在
handleRequest
方法中处理任何已检查的异常类型,并简单地将其包装,这是一个乏味的模板代码。因此,由于几乎所有使用Spring的产品都是为了让您尽可能轻松、轻巧地与之集成而设计的,因此它们更容易使用Spring
public void method(Parent p);