4

我正在开发一个应用程序,该过程像这样的 UI--> 后端进程--> 结果到 UI。

在我的代码中,我使用 try、catch 处理了我的异常。但是在代码中,我有很多重复的异常,可能会在不同的类中引发相同的异常。

所以,我打算做一个异常处理策略,以便在抛出异常时必须将其重定向到单独的异常处理代码(类似于单独的自定义异常处理库类)。所以它不应该在我的业务逻辑中。

谁能建议我如何实现它以及处理我的异常是否是最好的主意?或者任何人都可以建议我一种异常处理技术来做到这一点,而不是我的?

谢谢,期待您的回复。

4

2 回答 2

7

来自 Effective Java (Joshua Bloch)

  • 仅对异常情况使用 Exceptions(切勿用于普通控制流)
  • 对可恢复条件使用检查异常,对编程错误使用运行时异常。
  • 避免不必要地使用检查异常

避免检查异常。

http://www.mindview.net/Etc/Discussions/CheckedExceptions http://www.ibm.com/developerworks/java/library/j-jtp05254/index.html

  • 已检查的异常通常被许多人认为是一个有缺陷的功能。这不是一个坏主意,但人们并不真正了解如何使用它们。
  • Java 框架,如 Spring、Hibernate……它们大多都抛出未经检查的异常。
  • C# 没有故意实现检查异常。斯卡拉也没有。
  • 不是因为不检查就抓不到。
  • 不是不勾选就不能翻译(换行)
  • 这不是因为它未经检查就不是合同的一部分。实际上,您可以声明该方法以引发未经检查的异常。
  • 检查异常增加了客户端和库之间的耦合

要理解的一件非常重要的事情是任何一段代码都可能产生异常。 并不是因为一个方法声明抛出一个 IOException,它就不会抛出任何其他异常。它可以抛出任何其他运行时异常(通用或自定义)。对于已检查的异常,开发人员倾向于相反的想法,认为捕获 IOException 将处理所有异常情况,但事实并非如此!


仅编译时功能

当您忘记捕获或重新抛出已检查的异常时,只有编译器会告诉您。在运行时,没有区别。

这意味着通过使用类型擦除技巧,您可以抛出一个检查异常,甚至不需要它成为方法契约的一部分。

您可以在此处找到此技巧的示例,称为 SneakyTrow: https ://stackoverflow.com/a/4890489/82609

Lombok 还提供了一个 @SneakyThrow 注释来放置方法,这样您就不需要在方法签名中声明已检查的异常。


仅当您的类客户端可能从异常中恢复时才使用已检查异常。

这是太阳的建议。

基本上,连接到数据库的尝试将引发检查异常,并且重试策略代码将在连接尝试时捕获这些检查异常。当超过重试次数时,重试策略会抛出未经检查的异常,这意味着它不是可恢复的异常,因为恢复策略已经被尝试过。顺便说一下,您可以使用 Spring RetryTemplate 。


避免异常代码

异常类型应该足以用于流控制决策。解析异常或流控只会产生无用的代码。添加更多异常类型,就像您有异常代码一样。


快速失败

让所有不可恢复的异常都被抛出到 IHM 层。如果您使用声明检查异常的框架并且您没有针对它们的恢复策略,请不要犹豫将它们包装到未经检查的异常中。

如果你不能恢复,那么你不应该做“catch and log”。或者更糟糕的是,您不应该执行“捕获并返回 null”。这将产生不一致的软件,您可能稍后会在您的程序中引发另一个异常,但您将无法理解原因。返回 null 只会在以后创建 NullPointerException。

IHM 层技术可能具有异常处理程序/映射器。Web IHM 层具有异常映射器,因此您可以说“此异常产生 404 错误”。


功能方法

有关信息:在函数式语言中,使用异常进行流控制通常被认为是一种不好的做法。我们通常不会抛出异常,而是返回“增强类型”,例如 Either[Error,MyResultType]。

返回的实例是错误或成功,成功是返回的 MyResultType 实例。


异常对流控制不起作用

创建异常是有代价的(创建堆栈跟踪)。使用 if, else... 如果可以避免它们,请不要将它们用于流量控制。

基本上,您几乎总是可以避免使用它们,但在 Java 中,有时在某些情况下使用它们进行流控制可能更方便。在函数式语言中, Either monad 再次提供帮助。


使用断言

如果开发人员假设您的程序中的某些内容是真实的,请使用断言以保证您的断言是真实的。看看番石榴前提条件: https ://code.google.com/p/guava-libraries/wiki/PreconditionsExplained

或者,您可以使用 Java 原生断言或一些自定义代码。这有助于快速失败。

于 2013-04-23T14:33:03.893 回答
7

如果您抛出太低级别的异常,请在上面的几个调用中捕获它们,并使用用户友好的消息将它们打包为更一般和更有意义的异常出了什么问题,然后再次将它们抛出到可能的最高级别,在那里您停止您的应用程序并查看它们以某种方式(例如在标签上)给用户。

来自Effective Java(第 61 条

当一个方法抛出一个与它执行的任务没有明显联系的异常时,这是令人不安的。当方法传播由较低级别抽象引发的异常时,通常会发生这种情况。这不仅令人不安,而且还用实现细节污染了更高层的 API。如果更高层的实现在后续版本中发生变化,它抛出的异常也会发生变化,可能会破坏现有的客户端程序。

为了避免这个问题,较高层应该捕获较低级别的异常,并在它们的位置抛出异常,这些异常可以用较高级别的抽象来解释。这个成语被称为异常翻译:

// Exception Translation
try {
   // Use lower-level abstraction to do our bidding
   ...
} catch(LowerLevelException e) {
   throw new HigherLevelException(...);
}

虽然异常翻译优于从较低层无意识地传播异常,但不应过度使用它。在可能的情况下,处理来自较低层的异常的最佳方法是通过确保较低层方法成功来避免它们。有时,您可以通过在将较高级别方法的参数传递到较低层之前检查它们的有效性来做到这一点。

如果无法防止来自较低层的异常,则下一个最好的办法是让较高层静默处理这些异常,从而使较高层方法的调用者免受较低层问题的影响。在这些情况下,使用一些适当的日志记录工具(例如 java.util.logging)来记录异常可能是合适的。这允许管理员调查问题,同时将客户端代码和最终用户与它隔离开来。

总之,如果阻止或处理来自较低层的异常不可行,请使用异常转换,除非较低层的方法碰巧保证其所有异常都适用于较高层。链接提供了两全其美:它允许您抛出适当的更高级别的异常,同时捕获故障分析的根本原因(条款 63)。

于 2012-08-05T18:48:26.687 回答