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

Java 何时捕获异常与何时抛出异常?,java,exception,throw,Java,Exception,Throw,我用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. 所以目前我正在做的是——我在所有方法中抛出异常,并在方法A中捕获它,然后作为错误记录 但我不确定这样做是否正确?或者我应该开

我用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.
所以目前我正在做的是——我在所有方法中抛出异常,并在方法A中捕获它,然后作为错误记录

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


有没有人能给我一个详细的例子,说明什么时候捕获异常,什么时候抛出异常,这样我的概念就清楚了?在我的例子中,我是否应该继续抛出异常,然后在主调用方法A中捕获它?

通常,在您可以对它做一些有用的事情的级别上捕获它。例如,用户试图连接到某个数据库,但在方法D中失败

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

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

在web应用程序中,这可能会有所不同

一些非常简单的代码,大多数类都不存在,我也不确定DB的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
  }

}

当您使用知道该做什么的方法时,应该捕获异常

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

你有一门课,比如说:

public class FileInputStream extends InputStream {
    public FileInputStream(String filename) { }
}
现在,假设该文件不存在。你该怎么办?如果你在苦苦思索答案,那是因为没有答案。。。
FileInputStream
不知道如何解决该问题。所以它把它抛到链条上,即:

public class FileInputStream extends InputStream {
    public FileInputStream(String filename) throws FileNotFoundException { }
}
现在,假设有人在使用你的图书馆。他们可能有如下代码:

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);
        }
    }
}

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

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


e、 g无效的用户输入、数据库问题、网络中断、缺少文件

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

  • 如果您有连接到资源的方法(例如打开文件/网络),则捕获
  • 如果层次结构中较高的类需要有关错误的信息,则抛出

正如其他人所说,一般来说,当您能够实际处理异常时,您应该捕获它,否则,就抛出它


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

我将共享一个在一两个生产环境中保存了bacon的模式

动机

我的目标是确保在午夜试图解决sev1支持问题的可怜的家伙(可能是我)得到一个很好的由错误引起的层次结构,包括ID之类的数据,而不会使代码过于混乱

方法

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

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

这在现实生活中如何起作用的示例:

假设我的代码的工作是一个自动更新多份保险单的过程。该体系结构支持GUI手动触发一份保险单的更新。还可以说,其中一份保险单的DB中评级区域的邮政编码已损坏

我希望实现的错误日志类型的一个示例是

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

从堆栈跟踪:错误续订策略1234。正在回滚事务…此捕获还将覆盖诸如保存错误或生成信函等错误

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