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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/986695/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-29 14:41:37  来源:igfitidea点击:

In Java, is using throws Exception instead of throwing multiple specific exceptions good practice?

javaexceptionexception-handling

提问by James McMahon

While looking through the Spring MVC framework I noticed that, unless I misunderstand, its developers favor throws Exception instead of throwing multiple exceptions.

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

I realize that at the core of this question is the checked versus unchecked exceptions debate, avoiding that religious war, is it a good practice to use throws generic exception?

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

回答by Paul Tomblin

No, absolutely not. You should specify what exceptions you're going to throw so the caller can do the right thing with each one. If you don't, "throws Exception" gets passed up the chain, and the best the callers can do is printStackTrace() and die.

不,绝对不是。您应该指定要抛出的异常,以便调用者可以对每个异常做正确的事情。如果你不这样做,“抛出异常”就会被传递到链上,调用者能做的最好的事情就是 printStackTrace() 并死掉。

Update: To counter some of the "what if I override the method" objections, I'd go a bit further and say that any time you have a package that throws exceptions (as opposed to passing up an exception from a caller), you should declare an exception class in that package. Thus, if you're overriding my "addToSchedule() throws ScheduleConflictException", you're perfectly able to subclass ScheduleConflictException to do what you need.

更新:为了反驳一些“如果我重写方法会怎样”的反对意见,我会更进一步说,只要你有一个抛出异常的包(而不是传递来自调用者的异常),你应该在该包中声明一个异常类。因此,如果您要覆盖我的“addToSchedule() throws ScheduleConflictException”,您完全可以将 ScheduleConflictException 子类化来执行您需要的操作。

回答by matt b

What makes sense for a library such as Spring MVC, which needs to be open enough to fit all sorts of different use cases, does not necessarily make sense for you to follow when writing a specific application. This is one of those cases.

对于需要足够开放以适应各种不同用例的 Spring MVC 等库来说,什么是有意义的,但在编写特定应用程序时不一定对您有意义。这是其中一种情况。

If you are referring to classes such as the Controllerinterface which as a method signature such as

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

handleRequest(HttpServletRequest request, HttpServletResponse response) 
   throws Exception

This is likely because, from the perspective of the Spring classes which call into your Controller (such as DispatcherServlet), they don't care what type of Exception your code calls - library code such as DispatcherServletonly needs to know that this class may throw an Exception and therefore is capable of handling Exception in the general case.

这可能是因为,从Spring类,其呼叫到您的控制器(如角度DispatcherServlet),他们不关心什么类型的异常的代码调用-库代码,例如DispatcherServlet只需要知道这个类可能会抛出异常因此在一般情况下能够处理异常。

In other words, DispatcherServletdoesn't need to know what specific type(s) of Exception your Controller may throw - it's going to treat any of them as an "error". This is why the method signature is throws Exception.

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

Now, the API authors could have made the signature use a custom exception type as SpringMvcException, but that would only have the effect of forcing you to handle any checked exception types in your handleRequestmethod and simply wrap them, which is tedious make-work boilerplate code. So, since pretty much everything with Spring is designed to make it as easy and lightweight for you to integrate with as possible, it's easier for them to specify that the interface method merely throws Exception.

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

回答by Clint Miller

Here's the problem with throwing specific exceptions... Suppose someone extends your class and wants to override your method. Suppose their new implementation needs to throw a different type of exception. (How would you ever be able to predict what exceptions an overriding method might need to throw?) The person writing the overriding method only has two choices: 1) handle the exception himself (likely a bad choice), or 2) wrap the real exception in one of the allowed exception types and re-throw.

这是抛出特定异常的问题......假设有人扩展了您的类并想要覆盖您的方法。假设他们的新实现需要抛出不同类型的异常。(您如何能够预测覆盖方法可能需要抛出哪些异常?)编写覆盖方法的人只有两种选择:1)自己处理异常(可能是一个错误的选择),或者 2)包装真实的允许的异常类型之一中的异常并重新抛出。

But option 2 has two problems. First, when you dump your exceptions to your log files, you'll get long ugly chains of nested exceptions. More importantly, you'll lose your ability to catch specific exceptions. For example, suppose the overriding method calls another method that talks to the database and throws a DeadlockException if the resulting SQL caused a deadlock. The overriding method has to catch this exception, wrap it in one of the allowed types, and rethrow. This makes it impossible for code further up the stack to catch and detect the DeadlockException.

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

Your question ultimately gets into the heart of the debate about checked versus unchecked exceptions. You can Google and find lots of arguments for both sides of the debate. I think that ultimately, if you believe in checked exceptions, you should be very explicit about what exceptions a method throws. If you don't like checked exceptions, you should declare every method to throw Exception. I fall in the latter camp.

您的问题最终会成为关于受检异常与非受检异常的辩论的核心。你可以谷歌一下,找到很多争论双方的论据。我认为最终,如果您相信受检异常,您应该非常明确地说明方法抛出的异常。如果你不喜欢检查异常,你应该声明每个方法抛出异常。我属于后者。

By the way, for people who don't like checked exceptions, I don't like the idea of using RuntimeException's everywhere. The problem is that you'll likely need to incorporate a 3rd party library that uses Exception's rather than RuntimeException's. Then, your code will have to catch all Exception's from the library and wrap them in RuntimeException's. That creates a mess.

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

So, if I were starting a Java project from scratch again, I'd just declare every method to throw Exception.

所以,如果我再次从头开始一个 Java 项目,我只会声明每个方法来抛出异常。

回答by Tim

IMO the biggest reason for not throwing Exception is that it forces our clients to catch(Exception e)if they're truly interested in taking some action.

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

The problem with catch(Exception e)is that Exception, while checked, is also the superclass of RuntimeExceptionand so forces the client to catch all RuntimeExceptionstoo.

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

Sadly there is no catch(CheckedException e) technique. Assuming your poor client had no need to catch RuntimeExceptions, they have to perform an instanceof check and re-throw if its a RuntimeException.

遗憾的是没有 catch(CheckedException e) 技术。假设您可怜的客户端不需要捕获 RuntimeExceptions,他们必须执行 instanceof 检查并在它是 RuntimeException 时重新抛出。

回答by Aaron Maenpaa

No.

不。

Throwing Exception forces calling code to catch exception which they will probably not want to do for all kinds of reasons.

抛出异常会强制调用代码来捕获由于各种原因他们可能不想做的异常。

回答by Samuel Carrijo

IMHO, all design discussions and recommendations say good things about what should be done IN GENERAL, and they have a principle that support them. But usually these recommendations don't apply to all cases.

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

In the case of exception handling, the idea of using checked exceptions is that it is usually good to catch and manage the exception differently, depending on what you want. But, if you are sure they will all be caught and managed the same way, there's no point in checking the types all the time.

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

回答by samoz

In my opinion yes, because this allows you to throw any exception you want.

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

As your program grows, you might need to throw a different exception than you had originally thought about. If you defined each individual exception type, you would have to modify the methods signature and in turn modify all methods that called it to properly process the new exception.

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

Just using 'throws Exception' allows any method calling your method to properly handle any methods you have listed, but whenever you add new exceptions into your function, it won't break these other methods. (Though you should probably update them to handle the new exception, but this is not required.)

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

回答by Julien Chastang

Brian Goetz touches on some of these issues here. Also see Effective Java2nd Edition by Josh Bloch. He devotes a whole section on Exceptions. In addition, Rod Johnson, the founder of the Spring Framework, details his thoughts about Java Exception handling in J2EE Design and Development. You may or may not agree with his exception handling philospohy, but at least it may make more sense why they took the decisions they did.

Brian Goetz 在这里谈到了其中的一些问题。另请参阅Josh Bloch 的Effective Java2nd Edition。他用了一整节来讨论异常。此外,Spring Framework 的创始人 Rod Johnson 详细介绍了他在J2EE 设计和开发中对 Java 异常处理的看法。你可能同意也可能不同意他的异常处理理念,但至少他们做出决定的原因可能更有意义。

alt text
(source: sun.com)
alt text

替代文字
(来源:sun.com
替代文字

回答by Jay

You need to distinguish between generic code and more specific code.

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

Consider the return types of functions as an analogy. If you're writing a class like "ArrayList", you want it to be very general, so the parameters and return values are often generic "Object"s. But when you're writing code more specific to your application, like a "getRetiredEmployees" function, it would be a very bad idea to return Object. You more likely want to return Employee[] or something of that sort.

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

So sure, a framework like Spring is going to expect generic Exceptions because it doesn't have any idea what exceptions your particular application is going to throw. There's no way the authors of Spring could know that you were going to throw an "EmployeeNotInSpecifiedDepartment" exception or whatever. How could they? But if you're writing the sendPensionChecks function and you call getRetiredEmployees, you can reasonably expect to know specific exceptions that that function might throw, and what you should do to handle them.

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

Clint Miller brings up a valid point about not knowing how a class might be extended. I concede this is a problem with making your exceptions specific. But going from there to "just make everything a generic Exception" is giving up too easily. It's like saying that because someone someday might extend our getRetiredEmployees function to in some cases return EmployeeSpouse's along with Employee's, that therefore we should just give up and make the return type Object. If this is code you are using internally and you control, it's a non-problem: If you need to add a new Exception to a function, then add it. If this is an API that you are publishing to the world, then yes, the problem is much trickier. I'd say the general solution is to try to think out rationally what exceptions make sense and include them all, even if they aren't all presently implemented. If one of your clients is doing something completely unanticipated, oh well, that's a problem for which there is no easy answer.

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

Making everything generic is not the right answer, because it makes everything difficult to to understand, difficult to maintain, and difficult to verify accuracy.

使一切通用化并不是正确的答案,因为它使一切变得难以理解、难以维护和难以验证准确性。

回答by Gary Kleppe

In general, "throws Exception" is rarely a good idea.

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

In running computer code, runtime errors can be roughly put in two broad categories. There are those that are the result of bad code — maybe logic errors in the called method itself, or bad inputs having been passed to it. Then there are those problems that are due to circumstances beyond the programmer's control. Maybe the system is trying to open a file that another process has just deleted or access a remote server across a network that just went down.

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

If the first kind of exception happens, there's no reason for your program to catch it and continue. It should fail cleanly and obviously, making it clear what happened so that you can be alerted to fix whatever went wrong.

如果发生第一种异常,您的程序就没有理由捕获它并继续。它应该干净而明显地失败,清楚地说明发生了什么,以便您可以提醒您修复任何错误。

The second kind of exception is the kind you want to catch. You don't know whether these problems will occur on any given run, but you can have a pretty good idea of WHERE in your code they COULD occur -- basically, anytime you need to depend on a resource outside your program's control.

第二种异常是您想要捕获的异常。您不知道这些问题是否会在任何给定的运行中发生,但是您可以很好地了解它们可能发生在代码的何处——基本上,任何时候您需要依赖程序控制之外的资源。

Declaring thrown exceptions is essentially a contract between the authors of a method and the clients using it, telling you what you should expect could go wrong. A blanket "throws Exception" is equivalent to throwing up your hands and saying that clients must be ready for ANYTHING to go wrong, even problems that should've already been found and fixed during early debugging. Callers must either catch these exceptions -- and will usually just eat them and continue since the contract is so broad that they have no idea where the problem came from -- or throw them on upwards, forcing somebody else to deal with the same issues.

声明抛出的异常本质上是一个方法的作者和使用它的客户之间的契约,告诉你你应该期待什么可能会出错。一揽子“抛出异常”相当于举手说客户必须为任何出错做好准备,即使是在早期调试期间应该已经发现并修复的问题。调用者必须要么捕获这些异常——并且通常只会吃掉它们并继续,因为合同是如此广泛以至于他们不知道问题来自哪里——或者将它们向上抛出,迫使其他人处理相同的问题。