在代码中处理运行时异常不是一种好习惯吗?


11

我正在使用Java应用程序,并且看到在许多地方都可以处理运行时异常。例如,

try {
    // do something
} catch(NullPointerException e) {
    return null;
}

我的问题是,什么时候可以处理运行时异常?什么时候应该处理异常?


7
-1:对于这个广泛而模糊的问题,没有一个单一的答案。不可能提供如此单一的问题一个答案。这个问题太糟糕了。请提供您有疑问的具体示例。
S.Lott

@ S.Lott在这种情况下我有点不同意,因为似乎有一小部分程序员认为这是没有“良好”理性的情况。
SoylentGray 2011年

2
@乍得:“这是没有“良好”理性的情况”。可能是这样。但是唯一可能的答案必须是“取决于”。因此,这个问题似乎是错误的。
S.Lott

Answers:


18

这取决于。

例如,如果无法解析提供的字符串,则Integer#parseInt抛出NumberFormatException(RTE)。但是,您肯定不希望仅因为用户在用于整数的文本字段中输入“ x”而导致应用程序崩溃了吗?以及如何知道是否可以解析该字符串,除非您先尝试解析它?因此,在这种情况下,RTE只是一个错误信号,应该引起某种错误消息。有人可能会争辩说这应该是一个检查过的异常,但是您可以做什么-事实并非如此。


2
我同意“取决于”,但是您的解析示例似乎是API设计错误的情况。Integer#parseInt应该真正返回Maybe<Integer>而不是完全不抛出任何异常。
约尔格W¯¯米塔格

3
@JörgW Mittag:我同意这是糟糕的API设计,但这是我们必须面对的现实世界。如何正确处理可抛出/返回值取决于世界如何实际运作,而不是如何优化应该工作:-)
Joonas Pulakka

这里的重点是,您可以为预期的条件(用户输入不是整数)做一些有意义的事情。仅仅吞咽NPE是不好的样式,只会掩盖现有的编程错误。
于尔根斯特罗贝尔

7

NullPointerExceptions通常是缺少空检查的标志。因此,与其像这样捕获它,不如添加适当的null检查以确保不会引发异常。

但是有时,处理RunTimeExceptions是适当的。例如,当您不能修改代码以在适当的位置添加空检查时,或者当异常不是NullPointerException时。

您处理异常的示例非常糟糕。这样做会丢失堆栈跟踪和有关该问题的精确信息。而且您实际上并没有解决它,因为您可能会在其他地方触发另一个NullPointerException,并获得有关发生的情况以及如何解决它的误导性信息。


1
我想补充一点,空检查是一种更好的决策性能。
Mahmoud Hossam

...不过,如果您要在一个地方进行数十个“永不失败”的分配,那么为每个分配添加空值检查可能会使代码混乱。包罗万象的异常(将优雅地处理情况,而不仅仅是return null;)将是一个更好的解决方案。
SF。

您可能将Java与其他东西(例如C ++)混淆了。当分配失败时,您将收到OutOfMemoryError或类似内容,永远不会为空指针。隐藏一个空返回值而不是一个异常,就是隐藏错误,等待代码在其他地方爆炸。
deadalnix

new抛出std::bad_allocC ++。
R. Martinho Fernandes

假定新操作符未过载,这是常见的做法。在C ++中,任何事情都可能发生;)但是,好吧,我们说C的malloc。重要的一点是您在Java中从来没有得到过。
deadalnix 2011年

4

我在期望的地方处理期望的异常。(如DB读/写错误)。我冒出意外的异常。可能会有其他地方期望该异常并对此具有逻辑。


2

例外应该就是..例外。使用异常的最佳实践是使用它们来覆盖发生与您预期会发生的事情相反的情况。经典示例是FileNotFoundException,当文件根本不存在时会抛出该异常。如果要测试文件的存在性,则可以使用File.exists(),因为您只是用10英尺长的棍棒试探一下是否撞到了东西。

从技术上讲,您可以将其放在try catch中并使用该文件,就可以达到相同的结果,但是A)例外通常在资源上是昂贵的,B)程序员将假设您的意思是该文件存在(如果存在)尝试捕获,这增加了程序的整体混乱。

在很多情况下,我都会编写一个从数据库中获取一些值的方法。一千种方法可能会出错,并且看到我只需要一小部分信息,就很难用包含5种不同异常的try catch列表来包围该呼叫。因此,我将在fetch方法中捕获异常。如果出了问题,我将采取一切适当的措施来关闭数据库连接,或者在finally子句中关闭what,然后返回null。这是一个好习惯,不仅因为它简化了代码,而且因为“ null”发送的消息与您可能从异常中得到的信息相同。在提取方法中管理异常细节,但在事情不正常时管理该怎么办

例如:

Integer getUserCount() {
   Integer result = null;
   try {
      // Attempt to open database and retrieve data
   } catch (TimeoutException e) {
      logger.error("Got a watch?");
   } catch (MissingDatabaseException e) {
      logger.error("What are you smoking?");
   } catch (PermissionsToReadException e) {
      logger.error("Did you *really* think you were getting away with that?");
   } catch (PressedSendButtonToHardException e) {
      logger.error("Seriously.. just back away from the computer... slowly..");
   } catch (WTFException e) {
      logger.error("You're on your own with this one.. I don't even know what happened..");
   } finally {
      // Close connections and whatnot
   }
   return result;
}

void doStuff() {
   Integer result = getUserCount();
   if(result != null) {
       // Went as planned..
   }
}

6
如果您使用File.exists()并依赖结果,那么如果在File.exists()和File.open()之间删除该文件,则可能会发生竞争情况。如果这触发了一个关键性安全漏洞,则攻击者可能会故意导致这种情况。因此,有时最好保持原子操作,即尝试执行并捕获异常。
user281377 2011年

1
另外,还有一些文件必须存在才能运行应用程序。那将是例外情况。如果必须要有一个文件才能使应用程序正常运行,那么就没有理由不读取它,然后在特殊情况下处理异常。我相信这使意图更加明确。
Thomas Owens

这是返回null的错误决定。它将在某个时候导致NullPointerException,并且真的很难调试出了什么问题。因为有人会在某个时候忘记空检查。
deadalnix

@deadalnix:我可能会争辩说,否则您可能会很容易忘记忘记使用try-catch。差异是样式问题,而不是功能问题。
尼尔,

@ammoQ:我不同意。我认为您应该使用File.exists(),并且在极少数情况下会在使用它之前将其删除,因此异常情况是适当的。区别在于您保持捕获的位置。只需为意外的错误提供一个全面的异常捕获程序,以便对其进行记录和报告。
尼尔,

-5

是的,您正确地处理了运行时异常不是一个好习惯。原因是它被认为是昂贵/占用大量内存的。


5
嗨,普拉塞,你能详细说明你的答案吗?没有事实,参考或经验支持的一线员工并不是很有帮助。
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.