java—何时捕获异常与何时抛出异常?

smtd7mpg  于 2021-06-29  发布在  Java
关注(0)|答案(5)|浏览(593)

我用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中捕获它,然后作为错误记录。
但我不确定这样做是否正确?或者我应该开始捕捉所有方法中的异常。所以这就是为什么这种混乱开始于我的-我应该什么时候捕获异常和我应该什么时候抛出异常。我知道这是一个愚蠢的问题,但不知何故,我正在努力理解这个主要概念。
有人能给我一个详细的例子吗 When to catch the Exception vs When to throw the Exceptions 这样我的概念就清楚了?在我的例子中,我应该继续抛出异常,然后在主调用方法a中捕获它吗?

iibxawm4

iibxawm41#

当函数遇到故障(即错误)时,应该抛出异常。
功能是一个工作单元,故障应视为错误或基于其对功能的影响。在函数f中,失败是一个错误,当且仅当它阻止f满足被调用方的任何先决条件,实现f自己的任何后置条件,或重新建立f分担维护责任的任何不变量。
有三种不同类型的错误:
阻止函数满足另一个必须调用的函数的先决条件(如参数限制)的条件;
阻止函数建立自己的后置条件之一的条件(例如,产生有效的返回值就是后置条件);和
阻止函数重新建立它负责维护的不变量的一种状态。这是一种特殊的后置条件,特别适用于成员函数。每个非私有成员函数的一个基本后置条件是它必须重新建立其类的不变量。
任何其他情况都不是错误,不应报告为错误。
如果函数检测到无法自行处理的错误并阻止其以任何形式的正常或预期操作继续进行,则报告错误。
在有足够知识来处理错误、翻译错误或强制执行错误策略中定义的边界的地方处理错误,例如在主线或线程主线上。
来源:c++编码标准:101条规则、指南和最佳实践

mzsu5hc0

mzsu5hc02#

一般来说,在你能做一些有用的事情的水平上抓。例如,用户试图连接到某个数据库,但在方法d中失败。
你想怎么处理?可能会弹出一个对话框,说“对不起,无法连接到服务器/db”或其他什么。方法a、b或c是否创建了此服务器/db信息(例如,通过读取设置文件或请求用户输入)并尝试了连接?这可能就是应该处理异常的方法。或者至少与应该处理它的方法相差1。
它确实随应用程序的不同而变化,所以这只能是非常一般的建议。我的大部分经验是使用swing/桌面应用程序,你通常可以根据哪些类在做程序逻辑(例如“controller”之类的东西)和谁在放对话框(例如“view”之类的东西)来感觉。通常,“控制器”应该捕获异常并尝试执行某些操作。
在web应用程序中,这可能是不同的。
一些非常简单的代码,大多数类

svujldwt

svujldwt3#

我将分享一个模式,它在一两个生产环境中拯救了我的培根。
动机
我的目标是确保在午夜试图解决sev1支持问题的可怜的家伙(也许是我)得到一个很好的由错误引起的层次结构,包括id之类的数据,所有这些都不会使代码过于混乱。
方法
为了实现这一点,我捕获所有已检查的异常,并将它们作为未检查的异常重新抛出。然后,我在每个体系结构层的边界上使用一个全局捕获(通常是抽象的或注入的,所以它只写一次)。正是在这些点上,我可以向错误堆栈添加额外的上下文,或者决定是否记录并忽略,或者使用变量引发一个定制的checked异常来保存任何额外的上下文。另一方面,我只在顶层记录错误,以防止发生“双重记录”(例如cron作业,ajax的spring控制器)

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

使用这种方法,您的gui或业务层的方法签名不会因为必须为数据库相关的异常声明“throws”而变得混乱。
这在现实生活中是如何工作的一个例子:
假设我的代码的工作是一个自动更新许多保险单的过程。该体系结构支持gui手动触发一个策略的更新。也可以说,在这些策略之一的db中,评级区域的邮政编码已损坏。
我想实现的错误日志类型的一个例子是。
日志消息:由于以下错误,正在标记策略1234以进行手动干预:
从堆栈跟踪:更新策略1234时出错。正在回滚事务。。。这个catch还将覆盖诸如保存错误或生成一封信之类的错误。
从堆栈跟踪:原因:错误评级策略1234。。。这个捕获将拾取检索许多其他对象的错误,以及npe等算法错误。。。
从堆栈跟踪:原因:检索分级区域73932时出错。。。
来自堆栈跟踪:由:jpa:字段“postcode”中意外的null引起

3j86kqsm

3j86kqsm4#

当您处于知道要做什么的方法中时,应该捕获异常。
例如,暂时忘记它实际上是如何工作的,假设您正在编写一个用于打开和读取文件的库。
所以你要上课,比如说:

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

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

wqsoz72f

wqsoz72f5#

有两种情况需要捕获异常。

1. 在尽可能低的水平

这是您与第三方代码(如orm工具或任何执行io操作的库)集成的级别(通过http访问资源、读取文件、保存到数据库,您称之为)。也就是说,将应用程序的本机代码留在与其他组件交互的级别。
在这个级别上,可能会出现超出您控制的意外问题,例如连接失败和锁定的文件。
您可能希望通过捕获 TimeoutException 以便几秒钟后重试。访问文件时也会出现同样的异常,该文件此时可能会被进程锁定,但在下一时刻可用。
这种情况下的指导方针是:
只处理特定的异常,例如 SqlTimeoutException 或者 IOException . 从不处理泛型异常(类型 Exception )
只有当您有一些有意义的事情要做时才处理它,例如重试、触发补偿操作或向异常添加更多数据(例如上下文变量),然后重新抛出它
不要在此处执行日志记录
让所有其他异常冒出来,因为它们将由第二个案例处理

2. 尽可能高的水平

在将异常直接抛出给用户之前,这是最后一个可以处理异常的地方。
您的目标是记录错误并将详细信息转发给程序员,以便他们能够识别和更正错误。添加尽可能多的信息,记录下来,然后向用户显示道歉信息,因为他们可能对此无能为力,特别是如果这是软件中的错误。
第二种情况的指导方针是:
处理泛型异常类
从当前执行上下文添加更多信息
记录错误并通知程序员
向用户道歉
尽快解决

这些准则背后的理由

首先,异常代表不可逆的错误。它们表示系统中的错误、程序员犯的错误或应用程序无法控制的情况。
在这些情况下,用户通常很少或什么也做不了。因此,您唯一能做的就是记录错误,采取必要的补偿措施,并向用户道歉。如果这是程序员犯的错误,最好让他们知道并修复它,朝着更稳定的版本努力。
其次, try catch 块可以根据它们的使用方式屏蔽应用程序执行流。一 try catch 块的功能与 label 以及它的 goto companion,它使应用程序执行流从一点跳到另一点。

何时抛出异常

在开发库时更容易解释。当出现错误时,应该抛出错误,除了让api的使用者知道并让他们决定之外,您别无选择。
假设您是某个数据访问库的开发人员。当您遇到网络错误时,除了抛出异常之外,您什么也做不了。从数据访问库的Angular 来看,这是一个不可逆转的错误。
这是不同的,当你开发一个网站。您可能会捕获这样的异常以便重试,但是如果您从外层接收到无效参数,您可能会希望抛出一个异常,因为它们应该在那里得到验证。
这在表示层中也是不同的,您希望用户提供无效的参数。在这种情况下,您只需显示友好消息,而不是抛出异常。
作为在https://roaddd.com/the-only-two-cases-when-you-should-handle-exceptions/

相关问题