Java 何时捕获异常与何时抛出异常?

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

When to catch the Exception vs When to throw the Exceptions?

javaexceptionthrow

提问by AKIWEB

I have been coding in Java for a while now. But sometimes, I don't understand when I should throw the exception and when should I catch the exception. I am working on a project in which there are lot of methods. The hierarchy is something like this-

我已经用 Java 编码有一段时间了。但有时,我不明白什么时候应该抛出异常,什么时候应该捕获异常。我正在做一个有很多方法的项目。等级制度是这样的——

Method A will call Method B and Method B will call some Method C and Method C will call Method D and Method E.

So currently what I am doing is- I am throwing exceptions in all the methods and catching it in Method A and then logging as an error.

所以目前我正在做的是 - 我在所有方法中抛出异常并在方法 A 中捕获它,然后记录为错误。

But I am not sure whether this will be the right way to do it? Or should I start catching exceptions in all the Methods. So that is why this confusion started in my- When should I catch the Exception vs When should I throw the exceptions. I know it's a silly question but somehow I am struggling to understand this major concept.

但我不确定这是否是正确的做法?或者我应该开始在所有方法中捕获异常。所以这就是为什么这种混乱开始于我的 - 我什么时候应该捕获异常与什么时候应该抛出异常。我知道这是一个愚蠢的问题,但不知何故我很难理解这个主要概念。

Can someone give me a detailed example of When to catch the Exception vs When to throw the Exceptionsso that my concepts gets cleared on this? And in my case, should I keep on throwing the exception and then catch it in the main calling Method A?

有人可以给我一个详细的例子,When to catch the Exception vs When to throw the Exceptions以便我的概念得到澄清吗?就我而言,我应该继续抛出异常,然后在主调用方法 A 中捕获它吗?

回答by user949300

In general, catch at the level where you can do something useful about it. For example, user is trying to connect to some database, and it fails in Method D.

一般来说,抓住你可以做一些有用的事情的水平。例如,用户尝试连接到某个数据库,但在方法 D 中失败。

How do you want to handle it? Perhaps by putting up a dialog saying "Sorry, cannot connect to SERVER/DB" or whatever. Is is method A, B, or C that created this SERVER/DB information (say, by reading a settings file or asking for user input) and tried the connection? That is probablythe method that should handle the Exception. Or at least 1 away from the method that should handle it.

你想怎么处理?也许通过放置一个对话框说“对不起,无法连接到服务器/数据库”或其他什么。是方法 A、B 或 C 创建了此 SERVER/DB 信息(例如,通过读取设置文件或要求用户输入)并尝试连接?这可能是应该处理异常的方法。或者距离应该处理它的方法至少 1。

It really varies depending on your application, so this can only be very general advice. Most of my experience is with Swing / desktop apps, and you can usually get a feel based on which classes are doing program logic (e.g. "Controller" stuff) and who is putting up dialog boxes (e.g. "View" stuff). Usually the "controller" should catch the exception and try to do something.

它确实因您的应用程序而异,因此这只能是非常一般的建议。我的大部分经验是使用 Swing/桌面应用程序,您通常可以根据哪些类在执行程序逻辑(例如“控制器”内容)以及谁在放置对话框(例如“查看”内容)来感受。通常“控制器”应该捕获异常并尝试做一些事情。

In a web app this can be different.

在网络应用程序中,这可能有所不同。

Some very skeletal code, most of the classes do not exist, and Im not sure if a URL for the DB even makes sense, but you get the idea. Vaguely Swingish...

一些非常骨架的代码,大多数类都不存在,我不确定数据库的 URL 是否有意义,但你明白了。飘忽不定...

/*  gets called by an actionListener when user clicks a menu etc... */
public URL openTheDB() {
  URL urlForTheDB = MyCoolDialogUtils.getMeAURL(URL somePreviousOneToFillInTheStart);
  try {
     verifyDBExists(urlForTheDB);
     // this may call a bunch of deep nested calls that all can throw exceptions
     // let them trickle up to here

     // if it succeeded, return the URL
     return urlForTheDB;
  }
  catch (NoDBExeption ndbe) {
    String message = "Sorry, the DB does not exist at " + URL;
    boolean tryAgain = MyCoolDialogUtils.error(message);
    if (tryAgain)
      return openTheDB();
    else
      return null;  // user said cancel...
  }
  catch (IOException joe) {
    // maybe the network is down, aliens have landed
    // create a reasonable message and show a dialog
  }

}

回答by durron597

You should catch the exception when you are in the method that knows what to do.

当您在知道该做什么的方法中时,您应该捕获异常。

For example, forget about how it actually works for the moment, let's say you are writing a library for opening and reading files.

例如,暂时忘记它的实际工作原理,假设您正在编写一个用于打开和读取文件的库。

So you have a class, say:

所以你有一堂课,说:

public class FileInputStream extends InputStream {
    public FileInputStream(String filename) { }
}

Now, lets say the file doesn't exist. What should you do? If you're struggling to think of the answer, that's because there isn't one... the FileInputStreamdoesn't know what to do about that problem. So it throws it up the chain, i.e.:

现在,假设该文件不存在。你该怎么办?如果您在苦苦思索答案,那是因为没有答案……FileInputStream不知道如何处理这个问题。所以它把它扔到链上,即:

public class FileInputStream extends InputStream {
    public FileInputStream(String filename) throws FileNotFoundException { }
}

Now, lets say someone's using your library. They might have code that looks like this:

现在,假设有人在使用您的图书馆。他们可能有如下所示的代码:

public class Main {
    public static void main(String... args) {
        String filename = "foo.txt";
        try {
            FileInputStream fs = new FileInputStream(filename);

            // The rest of the code
        } catch (FileNotFoundException e) {
            System.err.println("Unable to find input file: " + filename);
            System.err.println("Terminating...");
            System.exit(3);
        }
    }
}

Here, the programmer knows what to do, so they catch the exception and handle it.

在这里,程序员知道该做什么,所以他们捕获异常并处理它。

回答by Sajan Chandran

You generally throws an exception when you want to notify the caller of the method of some failures.

当您想将某些失败的方法通知给调用者时,您通常会抛出异常。

e.g invalid user input, database problems, network outages, absent files

例如无效的用户输入、数据库问题、网络中断、文件缺失

回答by Marcin Szymczak

You should handle the exception at the lowest possible level. If method can't handle the exception properly you should throw it.

您应该在尽可能低的级别处理异常。如果方法不能正确处理异常,你应该抛出它。

  • catch If you have method which connects to resource (eg opens file/network)
  • throw if class higher in hierarchy needs information about error
  • 如果您有连接到资源的方法(例如打开文件/网络)
  • 如果层次结构中较高的类需要有关错误的信息,则抛出

回答by Martin Tuskevicius

As others have said, as a general rule, you should catch an exception when you can actually handle it, otherwise, just throw it.

正如其他人所说,作为一般规则,您应该在可以实际处理异常时捕获异常,否则就抛出它。

For example, if you are writing code that reads information about a connecting player from a save file and one of your I/O methods throws an IOException, then you would want to throw that exception and the code that invoked the loadmethod would want to catch that exception and handle it accordingly (like disconnect the player, or send a response to the client, etc.). The reason why you would not want to handle the exception in the loadmethod is because in the method, you cannot meaningfully handle the exception, so you delegate the exception to the caller in hope that they can handle it.

例如,如果您正在编写从保存文件中读取有关连接播放器的信息的代码,并且您的一个 I/O 方法抛出一个IOException,那么您可能希望抛出该异常,并且调用该load方法的代码将希望捕获该异常异常并相应地处理它(例如断开播放器,或向客户端发送响应等)。您不想在load方法中处理异常的原因是因为在方法中,您无法有意义地处理异常,因此您将异常委托给调用者,希望他们可以处理它。

回答by Tinman

I'll share a pattern that has saved my bacon in a production environments or two.

我将分享一种模式,它在一两个生产环境中保存了我的培根。

Motivation

动机

My aim is to ensure that the poor dude (maybe me)who is in at midnight trying to resolve a sev1 support ticket, gets a nice hierarchy of 'caused by' errors to follow, complete with data such as ID's, all without over cluttering the code.

我的目标是确保在午夜试图解决 sev1 支持票的可怜的家伙(也许是我)得到一个很好的“由”错误的层次结构,包括 ID 等数据,所有这些都不会过于混乱编码。

Method

方法

To achieve this, I catch all checked exceptions and re-throw them as unchecked exceptions. I then use a global catch at the boundary of each of my architectural layers (usually abstracted or injected so it is only ever written once). It is at these points that I can add extra context to the error stack, or decide whether to log and ignore, or raise a custom checked exception with variables to hold any extra context. On an aside, I only log errors at the top layer to stop 'double logging' from occurring (e.g. the cron job, the spring controller for ajax)

为了实现这一点,我捕获所有已检查的异常并将它们作为未检查的异常重新抛出。然后我在每个架构层的边界使用全局捕获(通常是抽象的或注入的,所以它只写一次)。正是在这些点上,我可以向错误堆栈添加额外的上下文,或者决定是否记录和忽略,或者使用变量引发自定义检查异常以保存任何额外的上下文。顺便说一句,我只在顶层记录错误以阻止“双重记录”的发生(例如,cron 作业、ajax 的 spring 控制器)

throw new RuntimeException(checked,"Could not retrieve contact " + id);

With this approach there is no cluttering of your GUI or business tier's method signatures by having to declare 'throws' for database related exceptions.

使用这种方法,您的 GUI 或业务层的方法签名不会因为必须为与数据库相关的异常声明“抛出”而变得混乱。

An Example of how this works in Real Life:

在现实生活中如何工作的一个例子:

Lets say my code's job is an automated process to renew many insurance policies. The architecture supports a GUI to manually trigger renewal for one policy. Lets also say that the postcode for the rating area is corrupted in the DB for one of these policies.

假设我的代码的工作是更新许多保险单的自动化过程。该架构支持 GUI 手动触发一项政策的续订。还可以说,对于这些策略之一,评级区域的邮政编码在数据库中已损坏。

An example of the type of error log I would want to achieve would be.

我想要实现的错误日志类型的一个例子是。

Log message: Flagging policy 1234 for manual intervention due to error:

From Stack Trace: Error Renewing Policy 1234. Rolling back the transaction ... This catch would also cover errors such as save errors, or generation of a letter.

From Stack Trace: Caused by: Error Rating Policy 1234 ... This catch would pickup errors retrieving many other objects, and algorithm errors such as NPE etc...

From Stack Trace: Caused by: Error Retrieving Rating Area 73932 ...

From Stack Trace: Caused by: JPA: unexpected null in field 'postcode'

日志消息:由于错误而标记策略 1234 以进行手动干预:

来自堆栈跟踪:错误更新策略 1234。回滚事务...... 此捕获还将涵盖诸如保存错误或信件生成等错误。

来自堆栈跟踪: 由:错误评级策略 1234 ......此捕获将拾取检索许多其他对象的错误,以及算法错误,例如 NPE 等......

从堆栈跟踪:引起:错误检索评级区域 73932 ...

从堆栈跟踪: 引起: JPA:字段“邮政编码”中的意外空值

回答by Frederik Krautwald

An exception should be thrown when a function experiences a failure, i.e., an error.

当函数遇到失败,即错误时,应该抛出异常。

A function is a unit of work, and failures should be viewed as errors or otherwise based on their impact on functions. Within a function f, a failure is an error if and only if it prevents ffrom meeting any of its callee's preconditions, achieving any of f's own postconditions, or reestablishing any invariantthat fshares responsibility for maintaining.

功能是一个工作单元,故障应被视为错误或基于它们对功能的影响。在一个函数˚F,失败是错误的,当且仅当它可以防止˚F不能满足其任何被叫者的先决条件,实现任何的˚F自己的后置条件,或者重新建立任何不变的是˚F维持股份责任。

There are three different kinds of errors:

存在三种不同类型的错误:

  • a condition that prevents the function from meeting a precondition (e.g., a parameter restriction) of another function that must be called;
  • a condition that prevents the function from establishing one of its own postconditions (e.g., producing a valid return value is a postcondition); and
  • a condition that prevents the function from re-establishing an invariant that it is responsible for maintaining. This is a special kind of postcondition that applies particularly to member functions. An essential postcondition of every non-private member function is that it must re-establish its class's invariants.
  • 阻止函数满足另一个必须调用的函数的先决条件(例如,参数限制)的条件;
  • 阻止函数建立其自己的后置条件之一的条件(例如,产生有效的返回值是后置条件);和
  • 阻止函数重新建立它负责维护的不变量的条件。这是一种特殊的后置条件,特别适用于成员函数。每个非私有成员函数的一个基本后置条件是它必须重新建立其类的不变量。

Any other condition is notan error and should not be reported as an error.

任何其他条件都不是错误,不应报告为错误。

Report an error wherever a function detects an error that it cannot deal with itself and that prevents it from continuing in any form of normal or intended operation.

在函数检测到自身无法处理的错误并阻止其继续以任何形式的正常或预期操作时,报告错误。

Handle the error in the places that have sufficient knowledge to handle the error, to translate it, or to enforce boundaries defined in the error policy, such as on mainor thread mainlines.

在有足够知识来处理错误、翻译错误或强制执行错误策略中定义的边界的地方处理错误,例如在或线程主线上。

回答by Phillippe Santana

There are two cases when you should catch an exception.

有两种情况您应该捕获异常。

1. At the lowest possible level

1. 在尽可能低的水平

This is the level at which you are integrating with third party code, such as an ORM tool or any library performing IO operations (accessing resources over HTTP, reading a file, saving to the database, you name it). That is, the level at which you leave your application's native codeto interact with other components.

这是您与第三方代码集成的级别,例如 ORM 工具或任何执行 IO 操作的库(通过 HTTP 访问资源、读取文件、保存到数据库,等等)。也就是说,您让应用程序的本机代码与其他组件交互的级别

At this level, unexpected problems out of your control such as connection failures and locked files may occur.

在此级别,可能会出现您无法控制的意外问题,例如连接失败和锁定文件。

You may want to handle a database connection failure by catching a TimeoutExceptionso that you can retryafter a few seconds. The same goes for an exception when accessing a file, which may be locked by a process at the moment but be available at the next instant.

您可能希望通过捕获 a 来处理数据库连接失败,TimeoutException以便您可以在几秒钟后重试。访问文件时的异常也是如此,该文件可能在当前被进程锁定,但在下一瞬间可用。

The guidelines in this scenario are:

这种情况下的指导方针是:

  • Handle only specific exceptions, such as SqlTimeoutExceptionor IOException. Never handle a generic exception (of type Exception)
  • Handle it only if you have something meaningful to do about it, such as retries, triggering compensatory actions, or adding more data to the exception (e.g. context variables), and then re-throw it
  • Do not perform logging here
  • Let all other exceptions bubble upas they will be handled by the second case
  • 仅处理特定的异常,例如SqlTimeoutExceptionor IOException。永远不要处理通用异常(类型Exception
  • 仅当您有一些有意义的事情要做时才处理它,例如重试、触发补偿操作或向异常添加更多数据(例如上下文变量),然后重新抛出它
  • 不要在此处执行日志记录
  • 让所有其他异常冒泡,因为它们将由第二种情况处理

2. At the highest possible level

2. 尽可能高的水平

This would be the last place where you can handle the exception before it is thrown directly to the user.

在将异常直接抛出给用户之前,这将是您可以处理异常的最后一个地方。

Your goal here is to log the error and forward the details to the programmersso they can identify and correct the error. Add as much information as possible, record it, and then show an apology message to the user, as there's probably nothing they can do about it, especially if it is a bug in the software.

您在这里的目标是记录错误并将详细信息转发给程序员,以便他们能够识别和纠正错误。添加尽可能多的信息,记录下来,然后向用户显示一条道歉信息,因为他们可能对此无能为力,尤其是如果它是软件中的错误。

The guidelines in this second scenario are:

第二种情况的指导方针是:

  • Handle the generic Exception class
  • Add more information from current execution context
  • Log the error and notify the programmers
  • Show an apology to the user
  • Work it out as soon as you can
  • 处理通用 Exception 类
  • 从当前执行上下文添加更多信息
  • 记录错误并通知程序员
  • 向用户道歉
  • 尽快解决

The reasoning behind these guidelines

这些指南背后的原因

First, is that exceptions represent irreversible errors. They represent represent a bug in the system, a mistake made by the programmers, or a situation beyond the control of the application.

首先,异常代表不可逆转的错误。它们代表系统中的错误、程序员犯的错误或应用程序无法控制的情况。

In these cases, there is usually little or nothing the user can do. Thus, the only thing you can do is log the error, take the necessary compensatory actions, and apologize to the user. If it is a mistake that the programmers made, it is best to let them know and fix it, working towards a more stable version.

在这些情况下,用户通常无能为力或无能为力。因此,您唯一能做的就是记录错误,采取必要的补偿措施,并向用户道歉。如果这是程序员犯的错误,最好让他们知道并修复它,朝着更稳定的版本努力。

Second, try catchblocks can mask application execution flowdepending on how they are used. A try catchblock has a similar function to that of a labeland its gotocompanion, which causes the application execution flow to jump from one point to another.

其次,try catch块可以根据它们的使用方式屏蔽应用程序执行流。阿try catch块具有类似的功能是alabel和它的goto同伴,这会导致应用程序的执行流程从一个点跳到另一个。



When to throw exceptions

何时抛出异常

Easier to explain in the context of developing a library. You should throw when you reached an error and there's nothing more you can dobesides letting the consumer of your APIs know about it, and letting them decide.

在开发库的上下文中更容易解释。您应该在遇到错误时抛出异常,除了让 API 的使用者知道并让他们做出决定之外,您无能为力

Imagine you're the developer of some data access library. When you reach a network error, there's nothing you can do besides throwing an exception. That's an irreversible error from a data access library standpoint.

假设您是某个数据访问库的开发人员。当您遇到网络错误时,除了抛出异常之外,您无能为力。从数据访问库的角度来看,这是一个不可逆转的错误。

This is different when you're developing a website. You would likely catch such exception in order to retry, but would want to throw an exception in case you received invalid parameters from outer layers since they should have been validated there.

这在您开发网站时有所不同。您可能会捕获此类异常以重试,但如果您从外层接收到无效参数,则希望抛出异常,因为它们应该在那里得到验证。

Which is again different in a Presentation layer, where you expect the user to provide invalid parameters. In that case you just show a friendly message instead of throwing an exception.

这在表示层中又有所不同,您希望用户提供无效参数。在这种情况下,您只需显示一条友好消息而不是抛出异常。



As featured at https://roaddd.com/the-only-two-cases-when-you-should-handle-exceptions/

正如https://roaddd.com/the-only-two-cases-when-you-should-handle-exceptions/