当前位置: 首页 > 面试题库 >

服务层中的已检查异常与未检查异常

申屠瀚海
2023-03-14
问题内容

我在一个带有旧服务层的项目上工作,如果请求的记录不存在,或者由于调用者未得到授权而无法访问,则在很多地方返回null。我说的是ID要求的特定记录。例如,类似:

UserService.get(userId);

最近,我一直在努力更改此API,或者用引发异常的新API进行补充。随之而来的是关于检查与未检查的异常的争论。

从JPA /
Hibernate等所有设计师的笔记中,我建议未检查的异常可能是最合适的。我的观点是,不能合理地期望API用户从这些异常中恢复,并且在99%的情况下,我们最多只能通知应用程序用户发生了一些错误。

将运行时异常传播到通用处理机制显然会减少很多复杂性,并减少处理边缘情况异常所需的分支处理。但是,围绕这种方法有很多担忧(正确地如此)。

为什么诸如JPA /
EJB和Hibernate之类的项目的设计人员选择使用未经检查的异常模型?有很好的理由吗?有什么优点/缺点。使用这些框架的开发人员是否应该仍在处理运行时异常,例如使用适配器包装程序抛出异常的地方?

我希望这些问题的答案可以帮助我们针对我们自己的服务层做出“正确”的决定。


问题答案:

尽管我同意未经检查的异常可以使API更加方便的观点,但我认为这并不是最重要的好处。而是这样的:

抛出未经检查的异常可以帮助您避免严重的错误。

这就是为什么。鉴于十个开发人员被迫处理受检查的异常,您将获得二十种不同的策略来处理它们,其中许多是完全不合适的。以下是一些较常见的错误方法:

  • 吞。 捕获异常并完全忽略它。即使应用程序现在处于不稳定状态,也可以继续进行,好像什么都没发生。
  • 记录并吞下。 捕获异常并记录下来,以为现在我们要负责了。然后继续前进,好像什么也没发生。
  • 神秘默认。 捕获异常,然后将某些字段设置为某个默认值,通常无需告知用户。例如,如果您无法加载某些用户角色,则只需选择一个低特权角色并进行分配即可。用户想知道发生了什么。
  • 愚蠢/危险的神秘预设。 捕获异常,并将某些字段设置为某些 非常差的 默认值。我在现实生活中看到的一个示例:无法加载用户的角色,因此继续尝试并发挥最佳作用(即,给他们一个高特权角色,以免给任何人带来不便)。
  • 错误报道。 开发人员不知道异常意味着什么,因此只提出自己的想法。IOException即使建立连接与该问题无关,也会变为“无法连接到服务器”。
  • 通过广泛的掩盖和误报。 尝试通过捕获Exception或(ugh)Throwable而不是方法实际引发的两个检查的异常来清理代码。异常处理代码不尝试将资源可用性问题(例如IOExceptions)与直接代码错误(例如NullPointerException)区分开。实际上,它经常会任意选择一个例外,并将每个例外都错误地报告为此类例外。
  • 通过大量尝试掩盖,并误报。 先前策略的一种变体是将一大堆异常声明调用放在单个大try块的范围内,然后捕获其中一个,Exception或者Throwable因为没有别的东西可以处理所有抛出的异常。
  • 抽象不合适的重新抛出。 即使该异常不适合抽象,也要抛出该异常(例如,从应该隐藏资源的服务接口中抛出与资源相关的异常)。
  • 重新包装而不包裹。 抛出一个异常(未检查或抽象适当的检查),但只需删除嵌套的异常,该异常将使任何人都有机会真正了解正在发生的情况。
  • 戏剧性的反应。 通过退出JVM来响应非致命异常。(感谢这篇博客文章。)

以我的经验,看到上面的方法比看到正确的答案要普遍得多。许多开发人员,甚至是“高级”开发人员,都有这样的想法:必须不惜一切代价抑制异常,即使这意味着在不稳定的状态下运行应用程序。这是危险的错误。

未经检查的异常有助于避免此问题。不知道如何处理异常的开发人员倾向于将异常视为要克服的不便之处,并且他们不会全力以赴地捕捉异常。因此,异常只会冒泡地到达顶部,在那里它们提供了堆栈跟踪,并可以以一致的方式对其进行处理。在极少数情况下,实际上要做的事要比使异常冒出来要好得多,没有什么可以阻止您的。



 类似资料:
  • 问题内容: 我在理解Java 和异常之间的区别时遇到了一些问题。 首先,异常应该在编译时寻找异常。在不同来源中提供的示例引用了数据库连接性,其中一些是文件处理,而异常应该是在程序员方面寻找错误,例如索引超出数组范围等。 不应该反过来吗?我的意思是,数据库连接是在运行时完成的,对吧?文件处理也是如此。您没有在编译时打开文件句柄,那么为什么在编译时会寻找一个可能的错误呢?另一方面,超出数组范围的索引已

  • 我有以下两个示例,我不清楚java.lang.Exception是如何处理的:作为检查的或未检查的异常。 以下方法编译成功: 在这里,我认为java.lang.Exception是威胁java.lang.RuntimeException或java.lang.Error。不处理也可以声明扔。 如果我们没有使用异常,而是使用了检查异常(它是java.lang.Exception的子类),那么您必须在方

  • 因为Java编程语言不需要捕获方法或声明未检查异常(包括 RuntimeException、Error及其子类),程序员可能会试图编写只抛出未检查异常的代码,或使所有异常子类继承自RuntimeException。这两个快捷方式都允许程序员编写代码,而不必担心编译器错误,也不用担心声明或捕获任何异常。虽然这对于程序员似乎很方便,但它避开了捕获或者声明异常的需求,并且可能会导致其他人在使用您的类而产

  • 问题内容: 约书亚·布洛赫(Joshua Bloch)在《有效的Java》中说 将检查的异常用于可恢复的条件,将运行时异常用于编程错误(第二版中的项目58) 让我们看看我是否正确理解了这一点。 这是我对检查异常的理解: 1.以上是否被视为经过检查的异常? RuntimeException是未经检查的异常吗? 这是我对未经检查的异常的理解: 4.现在,上面的代码难道不是一个检查过的异常吗?我可以尝试

  • 问题内容: 我在Java中有这个工厂方法: 我想将两个已检查的异常转换为未检查的异常。最好的方法是什么? 我是否应该仅捕获异常并使用捕获的异常作为内部异常抛出新的RuntimeException? 有没有更好的方法可以做到这一点?或者我应该首先尝试这样做吗? 编辑: 只是为了澄清。这些异常将是致命的,因为配置文件实质上是程序的运行,所有异常都将在程序的顶层捕获并记录。 我的目的是避免不必要的引发异

  • 问题内容: 据我了解,如果不逐一查找API文档,就无法找出方法抛出的异常。 由于这是没有选择的,因此我想撤消研究,并询问您在处理时遇到的最常见的Exception和RuntimeException: 铸件 数组 向量,ArrayList,HashMap等 IO(文件类,流,过滤器…) 对象序列化 线程(wait(),sleep()等) 或任何其他被视为“基本Java”的内容 我意识到这可能是主观的