【发布时间】:2009-12-30 21:13:41
【问题描述】:
在最近的一个项目中,我建议在测试工具代码中捕获 RuntimeException 并将其记录下来。该代码处理来自数据库的一系列输入,我不希望测试由于任何一个输入失败(空值、非法参数等)而停止。不用说,我的建议引发了热烈的讨论。
捕获任何类型的 RuntimeException 是否可以接受?如果是,还有哪些其他场景可以捕获 RuntimeExceptions?
【问题讨论】:
在最近的一个项目中,我建议在测试工具代码中捕获 RuntimeException 并将其记录下来。该代码处理来自数据库的一系列输入,我不希望测试由于任何一个输入失败(空值、非法参数等)而停止。不用说,我的建议引发了热烈的讨论。
捕获任何类型的 RuntimeException 是否可以接受?如果是,还有哪些其他场景可以捕获 RuntimeExceptions?
【问题讨论】:
您捕获RuntimeException 的原因与您捕获任何异常的原因相同:您打算对它做点什么。也许您可以纠正导致异常的任何原因。也许您只是想用不同的异常类型重新抛出。
捕获并忽略任何异常都是非常糟糕的做法。
【讨论】:
除非您可以更正 RuntimeException,否则您不想捕获它...
...仅从开发人员的角度来看是正确的....
您必须在它们到达 UI 之前捕获所有异常让您的用户感到难过。这意味着在“最高级别”上,您想要捕捉更进一步发生的任何事情。然后你可以让用户知道有问题,同时采取措施通知开发者,比如发送警报邮件什么的......
它基本上被认为是无法预见的数据/编程错误,因此您希望改进软件的未来版本,同时牵着用户的手并以受控方式继续前进......
【讨论】:
RuntimeException 旨在用于程序员错误。因此,它永远不应该被抓住。有几种情况应该是:
您正在调用来自第三方的代码,您无法控制它们何时抛出异常。我认为您应该根据具体情况执行此操作,并将第 3 方代码的使用包装在您自己的类中,这样您就可以传回非运行时异常。
您的程序不能崩溃并留下堆栈跟踪供用户查看。在这种情况下,它应该绕过 main 以及任何线程和事件处理代码。当发生此类异常时,程序也应该退出。
在您的具体情况下,我不得不质疑为什么您会在测试中出现 RuntimeExceptions - 您应该修复它们而不是解决它们。
所以你应该保证你的代码只在你想让程序退出时抛出 RuntimeExceptions。您应该只在想要记录并退出时捕获 RuntimeExceptions。这符合 RuntimeExceptions 的意图。
You can look at this discussion 出于人们给出的其他一些原因...不过,我个人在答案中还没有找到令人信服的理由。
【讨论】:
在我的代码中,99% 的异常源自 runtime_exception。
我捕获异常的原因是:
【讨论】:
几年前,我们编写了一个控制系统框架,并且代理对象捕获了运行时异常,如果可以,将它们记录下来并继续。
是的,我们在框架代码中捕获了 Runtime 异常,包括 OutOfMemory(并强制进行了 GC,令人惊讶的是,即使是非常泄漏的代码也能很好地保持运行。) 我们的代码在做涉及现实世界的非常数学的事情;由于微小的舍入误差,有时会出现一个非数字,但它也能很好地解决这个问题。
所以在框架/“不得退出”代码中,我认为这是合理的。当它起作用时,它非常酷。
代码相当可靠,但它运行硬件,硬件有时会给出错误的答案。
它的设计目的是在没有人工干预的情况下一次运行数月。 它在我们的测试中运行得非常好。
作为错误恢复代码的一部分,它可以借助 UPS 在 N 分钟内关闭并在 M 分钟内打开的能力来重新启动整个建筑物。
有时硬件故障需要重启:)
如果我记得,在一次不成功的电源循环后的最后手段是它向它的所有者发送一封电子邮件,说 “我试图修复自己,但我不能;问题出在子系统 XYZ 中”,并包含一个链接以向我们发起支持电话。
可悲的是,该项目在变得自我意识之前就被罐头了 :)>
【讨论】:
就我个人而言,我一直被告知要捕获所有 RuntimeExceptions;但是,您还希望对异常做一些事情,例如运行故障保护或可能只是通知用户发生了错误。
我从事的最后一个 Java 项目也有类似的方法,至少,我们会记录异常,以便如果用户打电话抱怨错误,我们可以准确找出发生的事情并查看错误发生的位置.
编辑1:正如kdgregory所说,捕捉和忽略是两件不同的事情,一般来说,人们反对后者:-)
【讨论】:
我们都知道检查异常和RuntimeExceptions是两类异常。总是建议我们处理(try-catch 或 throw)检查的异常,因为它们是编程条件,不幸的是程序员不能自己做任何事情;
与FileNotFoundException 一样,如果程序实际上正在尝试读取文件1.txt 应该存在于用户的f:\ 上,则不是程序员将文件放在用户的驱动器上:
File f11 = new File("f:\\1.txt");
FileInputStream fos = new FileInputStream(f11);
如果找到该文件,一切正常,但在另一种情况下,如果找不到该文件,则程序崩溃,用户错误为 0。在这种情况下,程序员没有做错任何事情。这可能是一个检查异常,必须捕获该异常才能使程序继续运行。
让我也解释一下RuntimeException 的概念将变得清晰的第二种情况。考虑以下代码:
int a = {1,2,3,4,5};
System.out.println(a[9]);
这是生成ArrayIndexOutOfBoundsException 的糟糕编码。这是RuntimeException 的一个例子。所以程序员不应该实际处理异常,让它崩溃程序,然后修复逻辑。
【讨论】:
当你想处理它时,你会捕获RuntimeException。也许您想将其作为不同的异常重新抛出或将其记录到文件或数据库中,或者您想在返回类型中打开一些异常标志等。
【讨论】:
当您的程序正在执行多个子任务时,您会捕获 RuntimeExceptions(在任何语言中:意外异常/“所有”异常),并且尽可能完成所有子任务而不是在第一个意外情况下停止是有意义的。测试套件是执行此操作的好时机——您想知道所有测试中的哪一个失败,而不仅仅是第一个测试。关键特征是每个测试都独立于所有其他测试 - 之前的测试是否没有运行并不重要,因为顺序并不重要。
另一种常见的情况是服务器;您不想仅仅因为一个请求以一种您没有预料到的方式格式不正确而关闭。 (除非将不一致状态的可能性降到最低是非常非常重要的。)
在任何这些情况下,适当的做法是记录/报告异常并继续执行剩余的任务。
人们可以模糊地概括为任何异常:当且仅当在捕获它之后有一些明智的事情要做时,它才“适合捕获”异常:你的程序应该如何继续。
【讨论】:
如果可以合理地期望客户端从异常中恢复,则将其设为已检查异常。
如果客户端无法从异常中恢复,请将其设为未经检查的异常。
这是底线指南。
来自 Java Docs。请阅读此Unchecked Exceptions — The Controversy
【讨论】: