捕获 Throwable 是一种不好的做法吗?

发布于 2024-11-08 12:58:16 字数 192 浏览 4 评论 0原文

捕获 Throwable 是一种不好的做法吗?

例如这样的事情:

try {
    // Some code
} catch(Throwable e) {
    // handle the exception
}

这是一种不好的做法还是我们应该尽可能具体?

Is it a bad practice to catch Throwable?

For example something like this:

try {
    // Some code
} catch(Throwable e) {
    // handle the exception
}

Is this a bad practice or we should be as specific as possible?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(14

﹉夏雨初晴づ 2024-11-15 12:58:16

您需要尽可能具体。否则,不可预见的错误可能会以这种方式消失。

此外, Throwable 涵盖错误 也是这样 通常没有返回点。您不想捕获/处理它,您希望您的程序立即终止,以便您可以正确修复它。

You need to be as specific as possible. Otherwise unforeseen bugs might creep away this way.

Besides, Throwable covers Error as well and that's usually no point of return. You don't want to catch/handle that, you want your program to die immediately so that you can fix it properly.

新人笑 2024-11-15 12:58:16

这是一个坏主意。事实上,即使捕获Exception通常也是一个坏主意。让我们考虑一个例子:

try {
    inputNumber = NumberFormat.getInstance().formatNumber( getUserInput() );
} catch(Throwable e) {
    inputNumber = 10; //Default, user did not enter valid number
}

现在,假设 getUserInput() 阻塞一段时间,另一个线程以最糟糕的方式停止您的线程(它调用 thread.stop() )。您的 catch 块将捕获 ThreadDeath 错误。这太糟糕了。捕获该异常后代码的行为很大程度上是未定义的。

捕获异常也会出现类似的问题。 getUserInput() 可能由于 InterruptException、尝试记录结果时出现权限被拒绝异常或各种其他故障而失败。你不知道出了什么问题,因此,你也不知道如何解决问题。

您有三个更好的选择:

1 -- 准确捕获您知道如何处理的异常:

try {
    inputNumber = NumberFormat.getInstance().formatNumber( getUserInput() );
} catch(ParseException e) {
    inputNumber = 10; //Default, user did not enter valid number
}

2 -- 重新抛出您遇到但不知道如何处理的任何异常:

try {
    doSomethingMysterious();
} catch(Exception e) {
    log.error("Oh man, something bad and mysterious happened",e);
    throw e;
}

3 -- 使用finally 块,这样您就不会不必记得重新抛出:

 Resources r = null;
 try {
      r = allocateSomeResources();
      doSomething(r);
 } finally {
     if(r!=null) cleanUpResources(r);
 }

This is a bad idea. In fact, even catching Exception is usually a bad idea. Let's consider an example:

try {
    inputNumber = NumberFormat.getInstance().formatNumber( getUserInput() );
} catch(Throwable e) {
    inputNumber = 10; //Default, user did not enter valid number
}

Now, let's say that getUserInput() blocks for a while, and another thread stops your thread in the worst possible way ( it calls thread.stop() ). Your catch block will catch a ThreadDeath Error. This is super bad. The behavior of your code after catching that Exception is largely undefined.

A similar problem occurs with catching Exception. Maybe getUserInput() failed because of an InterruptException, or a permission denied exception while trying to log the results, or all sorts of other failures. You have no idea what went wrong, as because of that, you also have no idea how to fix the problem.

You have three better options:

1 -- Catch exactly the Exception(s) you know how to handle:

try {
    inputNumber = NumberFormat.getInstance().formatNumber( getUserInput() );
} catch(ParseException e) {
    inputNumber = 10; //Default, user did not enter valid number
}

2 -- Rethrow any exception you run into and don't know how to handle:

try {
    doSomethingMysterious();
} catch(Exception e) {
    log.error("Oh man, something bad and mysterious happened",e);
    throw e;
}

3 -- Use a finally block so you don't have to remember to rethrow:

 Resources r = null;
 try {
      r = allocateSomeResources();
      doSomething(r);
 } finally {
     if(r!=null) cleanUpResources(r);
 }
迷爱 2024-11-15 12:58:16

另请注意,当捕获 Throwable 时,您还可以捕获 InterruptedException,这需要特殊处理。有关更多详细信息,请参阅处理 InterruptedException

如果您只想捕获未检查的异常,您也可以考虑这种模式。

try {
   ...
} catch (RuntimeException exception) {
  //do something
} catch (Error error) {
  //do something
}

这样,当您修改代码并添加可以抛出检查异常的方法调用时,编译器会提醒您,然后您可以决定要做什么对于这个案例。

Also be aware that when you catch Throwable, you can also catch InterruptedException which requires a special treatment. See Dealing with InterruptedException for more details.

If you only want to catch unchecked exceptions, you might also consider this pattern

try {
   ...
} catch (RuntimeException exception) {
  //do something
} catch (Error error) {
  //do something
}

This way, when you modify your code and add a method call that can throw a checked exception, the compiler will remind you of that and then you can decide what to do for this case.

月光色 2024-11-15 12:58:16

直接来自 Error 类的 javadoc(建议不要捕获这些):

ErrorThrowable 的子类,它指示合理的应用程序不应尝试捕获的严重问题。大多数此类错误都是异常情况。 ThreadDeath 错误虽然是“正常”情况,但也是 Error 的子类,因为大多数应用程序不应尝试捕获它。

方法不需要在其 throws 子句中声明在方法执行期间可能抛出但未被捕获的 Error 的任何子类,因为这些错误是不应该发生的异常情况。

<前><代码>@author 弗兰克·耶林
@版本%I%,%G%
@see java.lang.ThreadDeath
@从JDK1.0开始

straight from the javadoc of the Error class (which recommends not to catch these):

An Error is a subclass of Throwable that indicates serious problems that a reasonable application should not try to catch. Most such errors are abnormal conditions. The ThreadDeath error, though a "normal" condition, is also a subclass of Error because most applications should not try to catch it.

A method is not required to declare in its throws clause any subclasses of Error that might be thrown during the execution of the method but not caught, since these errors are abnormal conditions that should never occur.

@author  Frank Yellin
@version %I%, %G%
@see     java.lang.ThreadDeath
@since   JDK1.0
情魔剑神 2024-11-15 12:58:16

如果您使用的库过度热情地抛出错误,则有时需要捕获 Throwable,否则您的库可能会终止您的应用程序。

但是,在这些情况下,最好仅指定库抛出的特定错误,而不是所有 Throwables。

Catching Throwable is sometimes necessary if you are using libraries that throw Errors over-enthusiastically, otherwise your library may kill your application.

However, it would be best under these circumstances to specify only the specific errors thrown by the library, rather than all Throwables.

可是我不能没有你 2024-11-15 12:58:16

如果您绝对不能在方法中出现异常,那么这并不是一个坏习惯。

如果您确实无法处理异常,那么这是一个不好的做法。在方法签名中添加“抛出”比仅仅捕获并重新抛出更好,或者更糟糕的是,将其包装在 RuntimeException 中并重新抛出。

It's not a bad practice if you absolutely cannot have an exception bubble out of a method.

It's a bad practice if you really can't handle the exception. Better to add "throws" to the method signature than just catch and re-throw or, worse, wrap it in a RuntimeException and re-throw.

星星的軌跡 2024-11-15 12:58:16

问题有点模糊;您是否在问“是否可以捕获 Throwable”,或者“是否可以捕获 Throwable 并且不执行任何操作”?这里很多人都回答了后者,但这是一个次要问题; 99% 的情况下,您不应该“消耗”或丢弃异常,无论您是捕获 Throwable 还是 IOException 还是其他什么。

如果您传播异常,答案(就像许多问题的答案一样)是“视情况而定”。这取决于你对异常做了什么——为什么你要捕获它。

为什么要捕获 Throwable 的一个很好的例子是在出现任何错误时提供某种清理。例如,在 JDBC 中,如果在事务期间发生错误,您可能希望回滚事务:

try {
  …
} catch(final Throwable throwable) {
  connection.rollback();
  throw throwable;
}

请注意,异常不会被丢弃,而是会传播。

但作为一般策略,因为您没有理由并且懒得查看正在抛出哪些特定异常而捕获 Throwable 是糟糕的形式和坏主意。

The question is a bit vague; are you asking "is it OK to catch Throwable", or "is it OK to catch a Throwable and not do anything"? Many people here answered the latter, but that's a side issue; 99% of the time you should not "consume" or discard the exception, whether you are catching Throwable or IOException or whatever.

If you propagate the exception, the answer (like the answer to so many questions) is "it depends". It depends on what you're doing with the exception—why you're catching it.

A good example of why you would want to catch Throwable is to provide some sort of cleanup if there is any error. For example in JDBC, if an error occurs during a transaction, you would want to roll back the transaction:

try {
  …
} catch(final Throwable throwable) {
  connection.rollback();
  throw throwable;
}

Note that the exception is not discarded, but propagated.

But as a general policy, catching Throwable because you don't have a reason and are too lazy to see which specific exceptions are being thrown is poor form and a bad idea.

粉红×色少女 2024-11-15 12:58:16

Throwable 是所有可以抛出的类(不仅是异常)的基类。如果捕获 OutOfMemoryError 或 KernelError,您无能为力(请参阅何时捕获 java.lang.Error )。 lang.Error?

捕获异常应该就足够了。

Throwable is the base class for all classes than can be thrown (not only exceptions). There is little you can do if you catch an OutOfMemoryError or KernelError (see When to catch java.lang.Error?)

catching Exceptions should be enough.

第七度阳光i 2024-11-15 12:58:16

这取决于您的逻辑或更具体地了解您的选择/可能性。如果存在任何您可以以有意义的方式做出反应的特定异常,您可以首先捕获它并执行此操作。

如果没有,并且您确定会对所有异常和错误执行相同的操作(例如,带有错误消息的退出),那么捕获可抛出的异常就不是问题了。

通常第一种情况成立,你不会接住可投掷物。但仍有很多情况下捕捉它效果很好。

it depends on your logic or to be more specific to your options / possibilities. If there is any specific exception that you can possibly react on in a meaningful way, you could catch it first and do so.

If there isn't and you're sure you will do the same thing for all exceptions and errors (for example exit with an error-message), than it is not problem to catch the throwable.

Usually the first case holds and you wouldn't catch the throwable. But there still are plenty of cases where catching it works fine.

安静被遗忘 2024-11-15 12:58:16

尽管它被描述为一种非常糟糕的做法,但有时您可能会发现罕见情况下它不仅有用而且是强制性的。这里有两个例子。

在 Web 应用程序中,您必须向用户显示有意义的完整错误页面。
这段代码确保这种情况发生,因为它是一个围绕所有请求处理程序(servlet、struts actions 或任何控制器...)的大型 try/catch

try{
     //run the code which handles user request.
   }catch(Throwable ex){
   LOG.error("Exception was thrown: {}", ex);
     //redirect request to a error page. 
 }

}

作为另一个示例,假设您有一个服务提供资金转账业务的类。如果传输完成,此方法将返回 TransferReceipt;如果无法完成,则返回 NULL

String FoundtransferService.doTransfer( fundtransferVO);

现在想象一下,您获得了用户资金转账的列表,您必须使用上述服务来完成所有这些操作。

for(FundTransferVO fundTransferVO : fundTransferVOList){
   FoundtransferService.doTransfer( foundtransferVO);
}

但是如果发生任何异常会发生什么?您不应该停止,因为一笔转账可能成功,也可能不成功,您应该继续遍历所有用户List,并将结果显示给每次转账。所以你最终得到了这段代码。

for(FundTransferVO fundTransferVO : fundTransferVOList){
    FoundtransferService.doTransfer( foundtransferVO);
 }catch(Throwable ex){
    LOG.error("The transfer for {} failed due the error {}", foundtransferVO, ex);
  }
}

您可以浏览大量开源项目来查看 throwable 是否确实被缓存和处理。例如,这里是对 tomcatstruts2primefaces 的搜索:

https://github.com/apache/tomcat/search?utf8=%E2%9C%93&q=catch%28Throwable
https://github.com/apache /struts/search?utf8=%E2%9C%93&q=catch%28Throwable
https://github.com/primefaces /primefaces/search?utf8=%E2%9C%93&q=catch%28Throwable

Although it is described as a very bad practice, you may sometimes find rare cases that it not only useful but also mandatory. Here are two examples.

In a web application where you must show a meaning full error page to user.
This code make sure this happens as it is a big try/catch around all your request handelers ( servlets, struts actions, or any controller ....)

try{
     //run the code which handles user request.
   }catch(Throwable ex){
   LOG.error("Exception was thrown: {}", ex);
     //redirect request to a error page. 
 }

}

As another example, consider you have a service class which serves fund transfer business. This method returns a TransferReceipt if transfer is done or NULL if it couldn't.

String FoundtransferService.doTransfer( fundtransferVO);

Now imaging you get a List of fund transfers from user and you must use above service to do them all.

for(FundTransferVO fundTransferVO : fundTransferVOList){
   FoundtransferService.doTransfer( foundtransferVO);
}

But what will happen if any exception happens? You should not stop, as one transfer may have been success and one may not, you should keep go on through all user List, and show the result to each transfer. So you end up with this code.

for(FundTransferVO fundTransferVO : fundTransferVOList){
    FoundtransferService.doTransfer( foundtransferVO);
 }catch(Throwable ex){
    LOG.error("The transfer for {} failed due the error {}", foundtransferVO, ex);
  }
}

You can browse lots of open source projects to see that the throwable is really cached and handled. For example here is a search of tomcat,struts2 and primefaces:

https://github.com/apache/tomcat/search?utf8=%E2%9C%93&q=catch%28Throwable
https://github.com/apache/struts/search?utf8=%E2%9C%93&q=catch%28Throwable
https://github.com/primefaces/primefaces/search?utf8=%E2%9C%93&q=catch%28Throwable

违心° 2024-11-15 12:58:16

一般来说,您希望避免捕获 Error,但我可以想到(至少)两种适合这样做的特定情况:

  • 您希望关闭应用程序以响应错误,尤其是 AssertionError 否则是无害的。
  • 您是否实现了类似于 ExecutorService.submit() 要求您将异常转发回用户,以便他们可以处理它。

Generally speaking you want to avoid catching Errors but I can think of (at least) two specific cases where it's appropriate to do so:

  • You want to shut down the application in response to errors, especially AssertionError which is otherwise harmless.
  • Are you implementing a thread-pooling mechanism similar to ExecutorService.submit() that requires you to forward exceptions back to the user so they can handle it.
向日葵 2024-11-15 12:58:16

Throwable 是所有错误和异常的超类。
如果在 catch 子句中使用 Throwable,它不仅会捕获所有异常,还会捕获所有错误。 JVM 抛出错误来指示应用程序不打算处理的严重问题。典型的例子是 OutOfMemoryError 或 StackOverflowError。两者都是由应用程序无法控制且无法处理的情况引起的。因此,您不应该捕获 Throwables,除非您非常有信心它只会是 Throwable 内部的异常。

Throwable is the superclass of all the errors and excetions.
If you use Throwable in a catch clause, it will not only catch all exceptions, it will also catch all errors. Errors are thrown by the JVM to indicate serious problems that are not intended to be handled by an application. Typical examples for that are the OutOfMemoryError or the StackOverflowError. Both are caused by situations that are outside of the control of the application and can’t be handled. So you shouldn't catch Throwables unless your are pretty confident that it will only be an exception reside inside Throwable.

め七分饶幸 2024-11-15 12:58:16

如果我们使用throwable,那么它也会覆盖Error,仅此而已。

示例。

    public class ExceptionTest {
/**
 * @param args
 */
public static void m1() {
    int i = 10;
    int j = 0;
    try {
        int k = i / j;
        System.out.println(k);
    } catch (Throwable th) {
        th.printStackTrace();
    }
}

public static void main(String[] args) {
    m1();
}

}

输出:

java.lang.ArithmeticException: / by zero
at com.infy.test.ExceptionTest.m1(ExceptionTest.java:12)
at com.infy.test.ExceptionTest.main(ExceptionTest.java:25)

If we use throwable, then it covers Error as well and that's it.

Example.

    public class ExceptionTest {
/**
 * @param args
 */
public static void m1() {
    int i = 10;
    int j = 0;
    try {
        int k = i / j;
        System.out.println(k);
    } catch (Throwable th) {
        th.printStackTrace();
    }
}

public static void main(String[] args) {
    m1();
}

}

Output:

java.lang.ArithmeticException: / by zero
at com.infy.test.ExceptionTest.m1(ExceptionTest.java:12)
at com.infy.test.ExceptionTest.main(ExceptionTest.java:25)
七禾 2024-11-15 12:58:16

一个更有区别的答案是:这取决于情况。

异常和错误之间的区别在于,异常是一种必须预料到的状态,而错误是一种意外状态,通常是致命的。错误通常无法恢复,需要重置程序的主要部分甚至整个 JVM。

捕获异常是您应该始终执行的操作来处理可能发生的状态,这就是 JVM 强制执行的原因。 IE 打开文件可能会导致 FileNotFoundException,调用 Web 资源可能会导致 TimeoutException,等等。您的代码需要准备好处理这些常见情况。如何处理这些问题取决于您,无需从所有内容中恢复,但您的应用程序不应仅仅因为 Web 服务器需要更长的时间来响应而启动回桌面。

仅当确实有必要时才应执行捕获错误的操作。一般来说,您无法从错误中恢复,也不应该尝试这样做,除非您有充分的理由。捕获错误的原因是关闭否则会保持打开状态的关键资源,或者如果您有一台运行插件的服务器,则该服务器可以停止或重新启动导致错误的插件。其他原因是记录可能有助于稍后调试该错误的附加信息,在这种情况下,您当然应该重新抛出它以确保应用程序正确终止。

经验法则:除非您有重要原因要捕获错误,否则不要这样做。

因此,仅在非常重要的情况下才使用catch (Throwable t),否则坚持使用catch (Exception e)

A more differentiated answer would be: it depends.

The difference between an Exception and an Error is that an Exception is a state that has to be expected, while an Error is an unexpected state, which is usually fatal. Errors usually cannot be recovered from and require resetting major parts of the program or even the whole JVM.

Catching Exceptions is something you should always do to handle states that are likely to happen, which is why it is enforced by the JVM. I.E. opening a file can cause a FileNotFoundException, calling a web resource can result in a TimeoutException, and so on. Your code needs to be prepared to handle those situations as they can commonly occur. How you handle those is up to you, there is no need to recover from everything, but your application should not boot back to desktop just because a web-server took a little longer to answer.

Catching Errors is something you should do only if it is really necessary. Generally you cannot recover from Errors and should not try to, unless you have a good reason to. Reasons to catch Errors are to close critical resources that would otherwise be left open, or if you i.E. have a server that runs plugins, which can then stop or restart the plugin that caused the error. Other reasons are to log additional information that might help to debug that error later, in which case you of course should rethrow it to make sure the application terminates properly.

Rule of thumb: Unless you have an important reason to catch Errors, don't.

Therefore use catch (Throwable t) only in such really important situation, otherwise stick to catch (Exception e)

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文