当您可以捕获 Throwables 时,为什么要在 Java 中捕获异常?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/581878/
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-08-11 16:30:26  来源:igfitidea点击:

Why catch Exceptions in Java, when you can catch Throwables?

java

提问by Richard Corfield

We recently had a problem with a Java server application where the application was throwing Errors which were not caught because Error is a separate subclass of Throwable and we were only catching Exceptions.

我们最近在 Java 服务器应用程序中遇到了一个问题,该应用程序抛出的错误未被捕获,因为 Error 是 Throwable 的一个单独子类,我们只捕获异常。

We solved the immediate problem by catching Throwables rather than Exceptions, but this got me thinking as to why you would ever want to catch Exceptions, rather than Throwables, because you would then miss the Errors.

我们通过捕获 Throwables 而不是 Exceptions 解决了眼前的问题,但这让我思考为什么你会想要捕获 Exceptions 而不是 Throwables,因为你会错过错误。

So, why would you want to catch Exceptions, when you can catch Throwables?

那么,当您可以捕获 Throwables 时为什么要捕获 Exceptions呢?

采纳答案by Neil Coffey

It all depends a bit on what you're going to do with an Error once you've caught it. In general, catching Errors probably shouldn't be seen as part of your "normal" exception flow. If you do catch one, you shouldn't be thinking about "carrying on as though nothing has happened", because the JVM (and various libraries) will use Errors as a way of signalling that "something really serious has happened and we need to shut down as soon as possible". In general, it's best to listen to them when they're telling you the end is nigh.

这一切都取决于一旦你发现错误你将如何处理它。一般来说,捕获错误可能不应该被视为“正常”异常流的一部分。如果你确实抓住了一个,你不应该考虑“像什么也没发生一样继续”,因为 JVM(和各种库)将使用错误作为一种信号方式,“发生了非常严重的事情,我们需要尽快关闭”。一般来说,当他们告诉你末日临近时,最好听他们说。

Another issue is that the recoverability or not from an Error may depend on the particular virtual machine, which is something you may or not have control over.

另一个问题是错误的可恢复性与否可能取决于特定的虚拟机,这是您可能控制或无法控制的。

That said, there are a few corner cases where it is safe and/or desirable to catch Errors, or at least certain subclasses:

也就是说,在一些极端情况下,捕获错误或至少某些子类是安全和/或可取的:

  • There are cases where you really do want to stop the normal course of flow: e.g. if you're in a Servlet, you might not want the Servlet runner's default exception handler to announce to the world that you've had an OutOfMemoryError, whether or not you can recover from it.
  • Occasionally, an Error will be thrown in cases where the JVM can cleanly recover from the cause of the error. For example, if an OutOfMemoryError occurs while attempting to allocate an array, in Hotspot at least, it seems you can safely recover from this. (There are of course other cases where an OutOfMemoryError could be thrown where it isn't safe to try and plough on.)
  • 在某些情况下,您确实希望停止正常流程:例如,如果您在 Servlet 中,您可能不希望 Servlet 运行程序的默认异常处理程序向全世界宣布您遇到了 OutOfMemoryError,无论是你不能从中恢复。
  • 有时,如果 JVM 可以从错误原因中完全恢复,则会抛出错误。例如,如果在尝试分配数组时发生 OutOfMemoryError,至少在 Hotspot 中,您似乎可以安全地从中恢复。(当然,在其他情况下,可能会抛出 OutOfMemoryError,而尝试继续耕作是不安全的。)

So the bottom line is: if you do catch Throwable/Error rather than Exception, it should be a well-defined case where you know you're "doing something special".

所以底线是:如果你确实捕获了 Throwable/Error 而不是 Exception,它应该是一个明确定义的情况,你知道你正在“做一些特别的事情”

Edit: Possibly this is obvious, but I forgot to say that in practice, the JVM might not actually invoke your catch clauseon an Error. I've definitely seen Hotspot glibly gloss over attempts to catch certain OutOfMemoryErrors and NoClassDefFoundError.

编辑:这可能是显而易见的,但我忘了说在实践中,JVM 可能实际上不会在错误上调用您的 catch 子句。我肯定已经看到 Hotspot 巧妙地掩盖了捕获某些 OutOfMemoryErrors 和 NoClassDefFoundError 的尝试。

回答by Ferdinand Beyer

From the Java API documentation:

来自 Java API 文档:

The class Exceptionand its subclasses are a form of Throwablethat indicates conditions that a reasonable application might want to catch.

An Erroris a subclass of Throwablethat indicates serious problems that a reasonable application should not try to catch.

Exception及其子类是Throwable指示合理应用程序可能想要捕获的条件的一种形式。

AnError是一个子类Throwable,表示合理的应用程序不应尝试捕获的严重问题。

Errors usually are low-level (eg., raised by the virtual machine) and should not be caught by the application since reasonable continuation might not be possible.

错误通常是低级别的(例如,由虚拟机引发)并且不应被应用程序捕获,因为可能无法合理继续。

回答by Craig P. Motlin

Usually Errors are problems you cannot possibly recover from, like OutOfMemoryError. There's nothing to do by catching them, so you should usually let them escape, and bring down the virtual machine.

通常错误是您无法从中恢复的问题,例如OutOfMemoryError。捕捉它们没有什么可做的,所以你通常应该让它们逃脱,并关闭虚拟机。

回答by Johannes Weiss

Normally when programming, you should only catch a specific exception (such as IOException). In a lot of programs you can see a very toplevel

通常在编程时,您应该只捕获特定的异常(例如IOException)。在很多程序中你可以看到一个非常顶级的

try {
    ...
} catch(Exception e) {
    ...
}

That catches all errors which could be recoverable and all those which indicate a bug in your code, e.g. InvalidArgumentException, NullPointerException. You can then automatically send an eMail, display a message box or whatever you like, since the JavaVM itself is still working fine.

这会捕获所有可以恢复的错误以及所有表明代码中存在错误的错误,例如InvalidArgumentException, NullPointerException. 然后您可以自动发送电子邮件、显示消息框或任何您喜欢的东西,因为 JavaVM 本身仍然可以正常工作。

Everything derived from Erroris something very bad, you can't do anything against. The question is, if it makes sense to catch a OutOfMemoryErroror a VirtualMachineError. (It is a error in the JavaVM itself, probably you can't even display a message box or send an eMail then)

源自的一切都是Error非常糟糕的东西,你不能做任何反对。问题是,捕获 aOutOfMemoryError或 a是否有意义VirtualMachineError。(这是 JavaVM 本身的错误,可能您甚至无法显示消息框或发送电子邮件)

You should probably not a class derived from Error, you should derive from Exceptionor RuntimeException.

您可能不Error应该从派生一个类,而应该从Exception或派生RuntimeException

回答by cadrian

Slightly off topic, but you may also want to look at this very good articleabout exceptions.

稍微偏离主题,但您可能还想看看这篇关于异常的非常好的文章

回答by matt b

I know it might be counter-intuitive, but just because you can catch all sorts of Exceptions and Throwables and Errors does notmean you should.

我知道这可能是反直觉的,但仅仅是因为你可以捕捉各种异常和将Throwable和错误并不能意味着你应该。

Over-aggressive catching of java.lang.Exception can lead to some serious bugs in applications - because unexpected Exceptions never bubble up, are never caught during development/testing, etc.

过度捕获 java.lang.Exception 会导致应用程序中出现一些严重的错误 - 因为意外的异常永远不会冒泡,在开发/测试过程中永远不会被捕获,等等。

Best practice: only catch

最佳实践:只抓

  1. Exceptions that you can handle
  2. Exceptions that are necessary to catch
  1. 您可以处理的异常
  2. 需要捕获的异常

回答by James

Do NOT evercatch Throwableor Errorand you should generally not simply catch a generic Exceptioneither. Errors are generally things that most reasonable programs cannot possibly recover from. If you know what is going on, you might be able to recover from one specific error, but in that case, you should catch onlythat one particular error and not all errors in general.

永远不要捕获ThrowableorError并且您通常也不应该简单地捕获泛型ExceptionErrors 通常是最合理的程序不可能从中恢复的东西。如果您知道发生了什么,您也许能够从一个特定的错误中恢复,但在这种情况下,您应该捕获那个特定的错误,而不是一般的所有错误。

A good reason not to catch Erroris because of ThreadDeath. ThreadDeathis a fairly normal occurrence that can theoretically be thrown from anywhere (other processes like the JVM itself can generate it), and the whole point of it is to kill your thread. ThreadDeathis explicitly an Errorrather than an Exceptionbecause too many people catch all Exceptions. If you ever were to catch ThreadDeath, you mustrethrow it so that your thread actually dies.

不捕捉的一个很好的理由Error是因为ThreadDeathThreadDeath是一个相当正常的事件,理论上可以从任何地方抛出(其他进程,如 JVM 本身可以生成它),它的全部意义在于杀死您的线程。 ThreadDeath显然是 anError而不是 anException因为太多人抓住了所有Exceptions。如果您要捕获ThreadDeath,则必须重新抛出它,以便您的线程实际上死亡。

If you have control over the source, it should probably be restructured to throw an Exceptionrather than an Error. If you don't, you should probably call to the vendor and complain. Errors should be reserved for only things that are terminal with no possible way to recover from them.

如果您可以控制源,则可能应该对其进行重组以抛出一个Exception而不是Error. 如果你不这样做,你可能应该打电话给供应商并抱怨。 Errors 应该只保留给那些无法从中恢复的终端。

回答by Scott Stanchfield

I'll go a slightly different route from others.

我会走一条与其他人略有不同的路线。

There are many cases where you would want to catch Throwable (mainly to log/report that something evil happened).

在很多情况下,您想要捕获 Throwable(主要是为了记录/报告发生了一些邪恶的事情)。

However, you need to be careful and rethrow anything that you cannot deal with.

但是,您需要小心并重新抛出任何您无法处理的东西。

This is especially true of ThreadDeath.

ThreadDeath 尤其如此。

If you ever catch Throwable, be sure to do the following:

如果您曾经遇到过 Throwable,请务必执行以下操作:

try {
    ...
} catch (SomeExceptionYouCanDoSomethingWith e) {
    // handle it
} catch (ThreadDeath t) {
    throw t;
} catch (Throwable t) {
    // log & rethrow
}

回答by TofuBeer

This post won't make the "checked exceptions are bad" people happy. However, what I am basing my answer on is how Java exceptions are intended to be used as defined by the people that created the language.

这篇文章不会让“检查异常是坏的”人们高兴。但是,我的回答基于 Java 异常的使用方式,正如创建该语言的人所定义的那样。

Quick reference chart:

快速参考图表:

  • Throwable - never catch this
  • Error - indicates a VM error - never catch this
  • RuntimeException - indicated a programmer error - never catch this
  • Exception - never catch this
  • Throwable - 永远不要抓住这个
  • 错误 - 表示 VM 错误 - 从不捕获此错误
  • RuntimeException - 表示程序员错误 - 永远不会捕捉到这个
  • 例外 - 永远不要抓住这个

The reason you should not catch Exception is that it catches all of the subclasses, including RuntimeException.

不应该捕获 Exception 的原因是它会捕获所有子类,包括 RuntimeException。

The reason you should not catch Throwable is that it catches all of the subclasses, including Error and Exception.

不应该捕获 Throwable 的原因是它捕获了所有子类,包括 Error 和 Exception。

There are exceptions (no pun intended) to the above "rules":

上述“规则”有例外(无双关语):

  • Code you are working with (from a 3rd party) throws Throwable or Exception
  • You are running untrusted code that could cause your program to crash if it thew an exception.
  • 您正在使用的代码(来自第 3 方)抛出 Throwable 或 Exception
  • 您正在运行不受信任的代码,如果它出现异常,可能会导致您的程序崩溃。

For the second one usually it is enough to wrap main, event handling code, and threads with the catch to Throwable and then check the actual type of the exception and deal with it as appropriate.

对于第二个,通常将 main、事件处理代码和带有 catch 的线程包装到 Throwable 中,然后检查异常的实际类型并进行适当的处​​理就足够了。

回答by Ravi Wallau

There's at least one case when I think you may have to catch a throwable or a generic exception - if you're running a separate thread to perform a task, you may want to know if the "run" method of the thread has catched some exception or not. In that case, you probably will do something like this:

至少有一种情况,我认为您可能必须捕获可抛出的异常或通用异常 - 如果您正在运行一个单独的线程来执行任务,您可能想知道线程的“run”方法是否已经捕获了一些例外与否。在这种情况下,您可能会执行以下操作:


public void run() {
   try {
       ...
   }
   catch(Throwable t) {
       threadCompletionError = t;
   }
}

I am really not sure if it's the best approach, but it works. And I was having a "ClassNotFound" error being raised by the JVM, and it's an error and not an exception. If I let the exception be thrown, I am not sure how to catch it in the calling thread (probably there's a method but I don't know about it - yet).

我真的不确定这是否是最好的方法,但它有效。我有一个由 JVM 引发的“ClassNotFound”错误,这是一个错误而不是异常。如果我让异常被抛出,我不知道如何在调用线程中捕获它(可能有一个方法,但我还不知道它 - 尚)。

As for the ThreadDeath method, don't call the "Thread.stop()" method. Call Thread.interrupt and have your thread to check if it was interrupted by someone.

至于ThreadDeath方法,不要调用“Thread.stop()”方法。调用 Thread.interrupt 并让您的线程检查它是否被某人中断。