关于java:什么时候可以捕获RuntimeException

When is it OK to catch a RuntimeException


在最近的一个项目中,我建议在测试工具代码中捕获RuntimeException并记录它。 代码处理来自数据库的一系列输入,我不希望测试因任何一个输入失败而停止(Null值,非法参数等)。 不用说,我的建议引发了激烈的讨论。


捕获任何类型的RuntimeException都可以接受吗? 如果是,那么可以捕获RuntimeExceptions的其他方案是什么?



您捕获RuntimeException的原因与捕获任何异常的原因相同:您打算使用它执行某些操作。也许你可以纠正导致异常的任何事情。也许你只想用不同的异常类型重新抛出。


但是,捕捉并忽略任何异常都是非常糟糕的做法。



除非你能纠正RuntimeException,否则你不想抓住它......


......从开发人员的角度来看......


你必须在它们到达用户界面之前捕获所有异常并让你的用户感到悲伤。这意味着在"最高级别",您希望捕捉到进一步发生的任何事情。然后,您可以让用户知道存在问题,同时采取措施通知开发人员,例如发送警报邮件或其他...


它基本上被认为是一个无法预测的数据/编程错误,因此您希望改进软件的未来版本,同时让用户亲自操作并以受控方式继续...



RuntimeException旨在用于程序员错误。因此,它永远不应该被抓住。有几种情况应该是:


  • 您正在调用来自第三方的代码,在这些代码中,您无法控制何时抛出异常。我认为你应该根据具体情况这样做,并将第三方代码的使用包装在你自己的类中,这样你就可以传回非运行时异常。


  • 您的程序不会崩溃并留下堆栈跟踪供用户查看。在这种情况下,它应该围绕主要和围绕任何线程和事件处理代码。当发生此类异常时,该程序可能应该退出。


  • 在您的具体情况下,我将不得不质疑为什么您在测试中出现RuntimeExceptions - 您应该修复它们而不是解决它们。


    因此,当您希望程序退出时,您应该保证您的代码只会抛出RuntimeExceptions。只有在想要记录并退出时才应捕获RuntimeExceptions。这符合RuntimeExceptions的意图。


    你可以看看这个讨论是出于人们给出的其他一些原因......我个人在答案中没有找到令人信服的理由。



    几年前,我们编写了一个控制系统框架,Agent对象捕获了运行时异常,如果可以继续记录它们。


    是的,我们在框架代码中捕获了运行时异常(包括OutOfMemory)(并且强制使用GC,并且令人惊讶的是,即使运行的代码非常漏洞也是如此。)
    我们的代码在做涉及现实世界的非常数学的事情;由于微小的舍入误差,Not-A-Number会不时地进入,并且它也可以应对。


    所以在框架/"必须不退出"代码中我认为它是合理的。当它工作时它非常酷。


    代码非常可靠,但它运行硬件,硬件往往会给出棘手的答案。


    它的设计目的是在没有人为干预的情况下运行数月。
    它在我们的测试中表现得非常好。


    作为错误恢复代码的一部分,它可以使用UPS在N分钟内关闭并在M分钟内打开来重新启动整个建筑物。


    有时硬件故障需要重启电源:)


    如果我记得,电源循环失败后的最后一招是它发送电子邮件给它的所有者,说
    "我试图修复自己,我不能;问题出现在XYZ子系统中",并包含一个链接,向我们提出支持电话。


    可悲的是,这个项目在它变得自我意识之前得到了罐头:))



    在我的代码中,99%的异常都是从runtime_exception派生的。


    我捕获异常的原因是:

    • 捕获日志和修复问题。
    • 捕获日志并生成更具体的异常并抛出
    • 捕获日志并重新抛出。
    • 捕获日志和杀死操作(丢弃例外)

      • 用户/请求启动的操作失败。
        例如,HTTP请求处理程序。我宁愿所请求的操作死亡而不是降低服务。 (尽管处理程序最好有足够的意义来返回500错误代码。)
      • 测试用例通过/失败,但有异常。
      • 所有异常都不在主线程中。
        允许异常转义线程通常记录很差,但通常会导致程序终止(没有堆栈展开)。


    我们都知道检查异常和RuntimeExceptions是两类异常。总是建议我们处理(try-catch或throw)已检查的异常,因为它们是编程条件,不幸的是程序员不能自己做任何事情;
    FileNotFoundException一样,如果程序实际上正在尝试读取文件1.txt,那么程序员不会将文件放在用户的驱动器上,该文件应该位于用户的f:\上,并带有以下语句:

    1
    2
    File f11 = new File("f:\\1.txt");
    FileInputStream fos = new FileInputStream(f11);


    如果找到该文件就可以了,但是如果找不到该文件,则在另一种情况下发生的情况是,程序因用户的0错误而崩溃。在这种情况下,程序员没有做错任何事情。这可能是一个已检查的异常,必须捕获该异常以便程序继续运行。


    我还要解释第二种情况,RuntimeException的概念将是明确的。考虑以下代码:

    1
    2
    int a = {1,2,3,4,5};
    System.out.println(a[9]);


    这是编码很差的ArrayIndexOutOfBoundsException。这是RuntimeException的一个例子。所以程序员不应该实际处理异常,让它崩溃程序,然后修复逻辑。



    就个人而言,我总是被告知要捕获所有RuntimeExceptions;但是,您还希望对异常执行某些操作,例如运行故障安全或可能只是通知用户发生了错误。


    我工作的最后一个Java项目有一个类似的方法,至少,我们会记录异常,这样如果一个用户打电话抱怨一个bug,我们就可以确切地知道发生了什么,看看错误发生在哪里。


    编辑1:正如kdgregory所说,捕捉和忽略是两回事,一般来说,人们反对后者:-)



    当您想要处理它时,您会捕获RuntimeException。也许您想将它重新抛出为不同的异常或将其记录到文件或数据库,或者您想在返回类型中打开一些异常标志等。



    当程序执行多个子任务时,您会捕获RuntimeExceptions(使用任何语言:意外异常/"所有"异常),并且完成每个子任务都是有意义的,而不是在第一个意外情况下停止。测试套件是一个很好的情况 - 你想知道哪个测试失败了,而不仅仅是第一次测试。关键特性是每个测试都独立于所有其他测试 - 无论先前的测试是否未运行都无关紧要,因为订单无论如何都不重要。


    另一种常见情况是服务器;你不想因为一个请求以你没想到的方式出错而关闭。 (除非真的,最重要的是尽量减少状态不一致的可能性。)


    在任何这些情况下,适当的做法是记录/报告异常并继续执行其余任务。


    人们可以模糊地概括为任何例外情况:只有在捕获它之后才能做出明智的事情时,"适当地捕获"例外情况:你的程序应该如何继续。



    如果可以合理地期望客户端从异常中恢复,则将其作为已检查的异常。


    如果客户端无法执行任何操作以从异常中恢复,请将其设置为未经检查的异常。


    这是底线指南。


    来自Java Docs。请阅读此未经检查的例外 - 争议