10

我有一些代码将用户 ID 提供给实用程序,然后将电子邮件发送给该用户。

emailUtil.sendEmail(userId, "foo");

public void sendEmail(String userId, String message) throws MailException {
    /* ... logic that could throw a MailException */
}

MailException可能由于多种原因而引发,电子邮件地址问题,邮件模板问题等。

我的问题是:你是为这些异常中的每一个创建一个新的异常类型,然后单独处理它们,还是你创建一个 MailException 然后在允许的异常中存储一些东西(计算机可读的东西,而不是描述文本)我们根据实际发生的情况做不同的事情。

编辑:作为澄清,异常不适用于日志等等,这与代码对它们的反应方式有关。继续以邮件为例,假设当我们发送邮件时,它可能会因为您没有电子邮件地址而失败,或者因为您没有有效的电子邮件地址,或者它可能会失败......等等。

我的代码希望对这些问题中的每一个做出不同的反应(主要是通过更改返回给客户端的消息,但也更改实际逻辑)。

最好是为这些问题中的每一个问题或一个具有内部内容的总括异常(例如枚举)提供一个异常实现,让代码区分它是什么类型的问题。

4

11 回答 11

10

在我的代码中,我发现大多数异常会渗透到 UI 层,在那里它们被我的异常处理程序捕获,这些异常处理程序只是向用户显示一条消息(并写入日志)。毕竟,这是一个意外的例外。

有时,我确实想捕获一个特定的异常(正如您似乎想要做的那样)。但是,您可能会发现这有点少见,并且表明使用异常来控制逻辑——这是低效的(缓慢的)并且经常不受欢迎。

因此,使用您的示例,如果您想在未配置电子邮件服务器时运行一些特殊逻辑,您可能需要向 emailUtil 对象添加一个方法,例如:

公共布尔 isEmailConfigured()

...首先调用它,而不是寻找特定的异常。

当异常确实发生时,这确实意味着情况完全出乎意料并且代码无法处理它——所以你能做的最好的就是将它报告给用户(或将其写入日志或重新启动)

至于有一个异常层次结构与它们中带有错误代码的异常,我通常会做后者。如果您只需要定义一个新的错误常量而不是一个全新的类,那么添加新的异常会更容易。但是,只要您尝试在整个项目中保持一致,这并不重要。

于 2008-08-22T03:07:02.260 回答
8

我通常从一般异常开始,并根据需要对其进行子类化。如果需要,我总是可以捕获一般异常(以及所有子类异常),但也可以捕获特定异常。

Java-API 中的一个示例是 IOException,它具有 FileNotFoundException 或 EOFException(以及更多)等子类。

通过这种方式,您可以获得两者的优势,您没有像这样的抛出子句:

throws SpecificException1, SpecificException2, SpecificException3 ...

将军

throws GeneralException

足够。但是如果你想对特殊情况有特殊的反应,你总是可以捕捉到特定的异常。

于 2008-09-26T09:55:04.287 回答
2

@Chris.Lively

你知道你可以在你的异常中传递一条消息,甚至是“状态代码”。你在这里重新发明轮子。

于 2008-08-22T04:46:00.120 回答
2

我发现,如果您需要 CODE 根据返回的异常决定要做什么,请创建一个命名良好的异常子类化一个公共基类型。传递的信息应该被认为是“仅限人眼”,并且太脆弱而无法做出决定。让编译器完成工作!

如果您需要通过不知道已检查异常的机制将其传递给更高层,您可以将其包装在 RuntimeException (MailDomainException) 的一个合适的命名子类中,该子类可以被赶上高位,并根据原始原因采取行动。

于 2009-01-11T19:28:08.840 回答
1

这取决于您的应用程序在做什么。您可能希望在以下情况下抛出个别异常

  • 该应用程序具有高可用性
  • 发送电子邮件尤为重要
  • 应用范围小,发送邮件占很大一部分
  • 该应用程序将部署到远程站点,您只会获得用于调试的日志
  • 您可以从 mailException 中封装的某些异常子集中恢复,但不能从其他异常中恢复

在大多数情况下,我会说只记录异常的文本,不要浪费时间细化已经非常细化的异常。

于 2008-08-22T03:00:04.850 回答
1

我倾向于从可能有执行问题的方法返回状态对象列表,而不是使用异常。状态对象包含一个严重性枚举(信息、警告、错误……)一个状态对象名称,如“电子邮件地址”和一个用户可读的消息,如“格式错误的电子邮件地址”

然后调用代码将决定哪些过滤到 UI 以及哪些处理自己。

就个人而言,我认为异常严格适用于您无法实现正常代码解决方案的情况。性能影响和处理限制对我来说有点太多了。

使用状态对象列表的另一个原因是识别多个错误(例如在验证期间)要容易得多。毕竟,您只能抛出一个必须在继续之前处理的异常。

想象一下,一个用户提交了一封电子邮件,该电子邮件的目标地址格式错误,并且包含您阻止的语言。您是否抛出格式错误的电子邮件异常,然后在他们修复并重新提交后,抛出错误的语言异常?从用户体验的角度来看,一次处理所有这些是更好的方法。

更新:结合答案

@Jonathan:我的意思是我可以评估操作,在这种情况下发送电子邮件,然后发回多个失败原因。例如,“错误的电子邮件地址”、“空白邮件标题”等。

除了一个例外,您仅限于过滤一个问题,然后要求用户重新提交,此时他们会发现第二个问题。这真是糟糕的UI设计。

重新发明轮子……可能。但是,大多数应用程序应该分析整个事务,以便为用户提供尽可能好的信息。想象一下,如果您的编译器在第一个错误时停止工作。然后,您修复错误并再次点击编译,只是让它再次停止以出现不同的错误。屁股有多痛。对我来说,这正是引发异常的问题,因此我说要使用不同的机制。

于 2008-08-22T04:35:53.297 回答
1

我认为以上的组合会给你最好的结果。

您可以根据问题抛出不同的异常。例如,缺少电子邮件地址 = ArgumentException。

但随后在 UI 层中,您可以检查异常类型,如果需要,还可以检查消息,然后向用户显示适当的消息。我个人倾向于仅在引发某种类型的异常(我的应用程序中的 UserException)时才向用户显示信息性消息。当然,您应该尽可能多地清理和验证用户输入,以确保任何异常都是由真正不可能的情况生成的,而不是作为可以使用正则表达式轻松检查的格式错误电子邮件的过滤器。

我也不担心从用户输入中捕获异常对性能的影响。您唯一会看到异常导致性能问题的情况是当它们被抛出并陷入循环或类似情况时。

于 2008-08-26T02:03:14.143 回答
0

我倾向于使用较少的异常类型,尽管这并不是真正的 OO 方式。相反,我在我的自定义异常中添加了一个枚举,它对异常进行了分类。大多数时候我有一个自定义的基础异常,它包含几个成员,可以在派生的异常类型中覆盖或自定义。

几个月前,我在博客上写了关于如何将异常国际化的想法。它包括上面提到的一些想法。

于 2008-08-22T05:42:56.410 回答
0

虽然您可以通过“catch exceptionType 层次模式”或“if(...) else...exception code mode”来区分查看异常的代码执行,但这并不重要

但是,如果您正在开发将被其他人使用的软件,例如一个库,我认为创建自己的异常类型以通知其他人您的软件可以抛出其他异常而不是正常异常是有用的,他们更好地捕捉和解决它们。

当我使用一个库并且他们的方法只是启动一个“异常”时,我总是想知道:什么会导致这个异常?,我的程序必须如何反应?,如果有 javadoc 可能会解释原因,但必须有不是 javadoc 或未解释异常。使用 WellChossenExceptionTypeName 可以避免过多的开销

于 2008-08-22T15:13:56.227 回答
0

这取决于捕获异常的代码是否需要区分异常,或者您是否只是使用异常来失败到错误页面。如果您需要区分 NullReference 异常和调用堆栈中较高的自定义 MailException,请花时间编写它。但大多数时候,程序员只是使用异常作为包罗万象的方法来在网页上抛出错误。在这种情况下,您只是在浪费精力编写新的异常。

于 2008-08-26T01:57:30.990 回答
-1

我会过去的

throw new exception("WhatCausedIt")

如果要处理异常,可以传递代码而不是“WhatCausedIt”,然后使用 switch 语句对不同的答案做出反应。

于 2008-08-22T05:48:25.447 回答