【问题标题】:Java Error handling - is it better to throw Exceptions to centralized error handlers?Java 错误处理 - 向集中式错误处理程序抛出异常是否更好?
【发布时间】:2012-04-16 18:04:54
【问题描述】:

我有一个相当复杂的 Java 小游戏(纸牌,基本上类似于 Windows 版本),但我还没有做很多错误处理。

我的类中的几乎所有方法最终都会被初始构造函数(最终是 main())、paintComponent() 方法或鼠标事件调用。所以,我的问题是,只在我所有的低级方法上使用“抛出异常”,并且只在我的顶级方法上执行 try/catch 来一次捕获所有错误,这是不好的做法吗? (例如 3 个尝试/捕获 - 一个用于绘画,一个用于鼠标事件,一个用于主要方法)。

我意识到这使我无法轻松地在现场处理错误,但我并不打算这样做。我的错误处理将包括写入日志、告诉用户和终止程序。记住这一点,以这种方式进行错误处理有什么不好的吗?

【问题讨论】:

标签: java error-handling


【解决方案1】:

这取决于你想如何处理这种情况。
如果您只想捕获任何可能的异常并且您不介意处理程序代码,您可以简单地使用“抛出异常”,它也没有什么不好的。它就像一个覆盖所有功能的 try-catch 块。
如果您想为特定异常编写特定代码,您应该使用 try-catch 块为每个处理程序编写适当的代码。
根据您所说的,任何捕获的异常都会通知用户并退出应用程序。好吧,在这种情况下,您可以只使用第一种方法。这不一定是最好的方法,也不是杀死应用程序,但是,如果这是您的策略,您可以对每个函数使用“抛出”。
希望对您有所帮助!

【讨论】:

  • 我喜欢在这个答案中强调在“现在足够好”和“正确的方式”之间进行选择。干得好,@V0R73X
【解决方案2】:

如果发生错误时您不想这样做,那么这样做是非常有意义的。这消除了代码重复和相关代码的分散。但是,如果您正在考虑改变未来的工作方式(如果有可能发生这种情况),我建议您尝试尽可能地降低捕获量(甚至可能完全消除对异常的需要并且只需登录并立即退出)。

如果您使用异常的内部字段(特别是 message,您可以在构造时设置),您甚至可以消除对 3 个不同的 catch 块的需要,而只使用一个(取决于您在发生异常时的实际操作)当然是错误)。

【讨论】:

    【解决方案3】:

    我不会 - 主要原因是它破坏了封装。 为什么在这种情况下很重要的原因是您的错误处理代码具有两个未来之一:

    1. 变得庞大,以一种信息丰富的方式处理程序可能抛出的每个错误。
    2. 小而无用:“某处发生了一些错误”。

    在我看来,最好的结构是捕获错误、记录错误、提醒用户并尽可能地退出。没有什么说你的鼠标处理代码不能退出,对吧?

    事实上,我会创建一个错误处理程序类,您可以从几乎任何地方调用它,它处理通知和日志记录。然后,您的异常处理程序可以使用要显示/记录的消息填充它,并且所有其他代码都是共享的。与在每个函数的末尾添加 throws Exception 相比,委托给此类将花费您更少的输入。

    如果您必须有一个顶级处理程序,它应该只捕获任何意外的运行时错误,以便您可以记录它并向用户显示程序确实因错误而退出,而不仅仅是转储到桌面。所有其他错误——即使您只是想摆脱困境——也应该在尽可能接近“异常有意义的地方”被捕获。

    【讨论】:

    • 据我所知,使用Java,错误信息在抛出一个级别时不会丢失。如果我在低级对象中抛出异常,我仍然可以访问捕获错误的高级对象中的所有细节错误信息。所以它不会是“某处发生了一些错误”——它将能够具体告诉我发生了什么以及发生在哪里
    • 嗯,这是一个很好的观点,而且是真的。这取决于您的异常处理的粒度。根据依赖倒置原则,您在泛型处理程序中所能做的就是调用泛型 Exception 类上定义的方法 - 例如getMessage。如果这足够好,那就足够好了。然而,如果你想处理一个 MouseException,用一个愚蠢的例子,告诉用户“插入你的鼠标,傻瓜”,泛型处理永远不能清楚地告诉你除了泛型异常之外还有任何东西。跨度>
    • 根据您当前的设计,这似乎很好 - 但我个人知道我的异常处理方法几乎立即开始闻起来,因为根据定义,异常是“某处不正常”。虽然我喜欢 YAGNI,但我倾向于从一开始就过度设计异常,这样我就不会创建一个我永远无法逃脱的大泥球 - 重构异常处理代码是一个真正的 PITA。
    【解决方案4】:

    我大部分时间都在做和你描述的一样的事情。您基本上在做的是解决 Java 具有的愚蠢的检查异常。甚至没有必要在任何地方添加“抛出异常”。

    如果我正在开发其他用户将使用的 API,我可能会创建特定的异常来显示正在发生的事情,以防他们想以不同的方式处理不同的异常。

    【讨论】:

    • 我不知道 - 我认为 throws Exception 的痛苦是故意的 - 告诉你尽可能低地捕获异常并在那里处理它,即使处理是调用barf 函数。
    • 我经常看到的是异常最终被静音,代码继续执行,然后在不同的地方抛出 NPE 或另一个异常。当这种情况发生时,我们再次进行 C 错误处理:您为所做的一切编写一行代码和 4 行错误处理。 Exceptions 应该解决这个问题,这样您就不会在您的应用程序中到处乱扔错误处理代码,并且仅在您真正想要对其执行某些操作的地方,例如记录异常 - 您只需要在代码开始执行。
    • 我也经常看到这种情况,但这是一个“异常与运行时异常”的反模式。 Java 对Exception 做了严格区分,Exception 是“可修复的”并且应该 被处理(例如 FileNotFoundException: re-ask the user for the path)和RuntimeExceptions,它们真的是错误(例如 NullPointerException)。 RuntimeExceptions 不需要“抛出”声明。如果我想忽略一个异常,我会重新抛出它作为 RuntimeException 的原因,这更清晰(使用有效的堆栈跟踪),更面向未来(我可以一次修复一个),并且不会混淆函数接口。
    【解决方案5】:

    如果错误严重到总是退出程序,则最好抛出 RuntimeException。它们用于指示不可恢复的错误,并将避免将“抛出异常”放在任何地方的问题。无论如何,您都应该有一个 RuntimeExceptions 处理程序,以便在它们发生时提供用户友好的错误报告。

    如果你抛出检查异常,它们应该尽可能具体。抛出和捕获 Exception 可以隐藏抛出的其他异常(包括 RuntimeExceptions),这些异常是您不打算的并且可能以不同方式处理。如果你想抛出一个一般的异常,你总是可以创建自己的异常类并抛出它。

    异常处理可以依赖于上下文,所以没有一种方法可以处理所有事情。如果用户单击按钮打开文件并且读取文件时出错,则可以向 UI 层抛出 IOException 并在那里显示错误消息。另一方面,创建临时文件时的 IOException 可以通过在另一个目录中重试来处理。

    【讨论】:

      猜你喜欢
      • 2011-04-07
      • 1970-01-01
      • 1970-01-01
      • 2011-08-17
      • 1970-01-01
      • 2015-03-29
      • 1970-01-01
      • 1970-01-01
      • 2013-11-03
      相关资源
      最近更新 更多