【问题标题】:Is it OK to try/catch something just to check if an exception was thrown or not?尝试/捕获某些东西只是为了检查是否引发了异常是否可以?
【发布时间】:2011-04-10 16:33:31
【问题描述】:

尝试一些无用的东西只是为了看看这段代码是否抛出了一个特定的异常,这是一个好方法吗?
我想在抛出异常时做点什么,否则什么都不做。

try {  
    new BigDecimal("some string"); // This do nothing because the instance is ignored  
} catch (NumberFormatException e) {  
    return false; // OK, the string wasn't a well-formed decimal  
}  
return true;

要测试的前置条件太多了,构造函数BigDecimal()总是检查它们,所以这似乎是最简单的方法。

【问题讨论】:

  • 你问这个很好。这意味着您认识到要避免这种模式,但您看不到其他选择。 +1
  • 这不是问题的答案,但你的例子是一个极好的内存泄漏......可能会有异常被用作函数的返回的情况,但不是你应该忘记返回的地方新的。
  • @Martin:是的,你是对的。我不专心,以为是 C++ 而不是 Java 有垃圾收集。

标签: java exception


【解决方案1】:

一般来说,应该避免这种做法。但是由于没有实用方法isValidBigDecimal(..),那就是要走的路。

正如 Peter Tillemans 在 cmets 中指出的,将此代码放在名为 isValidBigDecimal(..) 的实用程序方法中。因此,您的代码将不知道确定有效性的方式,您甚至可以稍后切换到另一种方法。

Boris Pavlović 建议使用 3rd 方库 (commons-lang) 进行检查。还有一种更有用的方法,我在需要验证数字时使用它 - NumberUtils.isNumber(..)

【讨论】:

  • +1,特别是如果你将它封装在一个漂亮的小函数中,并且具有一个有意义的名称!lie isValidBigDecimal),以便在出现更好的技术时可以轻松地交换它。
  • 这样小的实用方法在实现为 C# 扩展方法时非常方便。
【解决方案2】:

这样做没有错;毕竟,某些其他语言的支持者喜欢说“道歉比请求许可更容易”,也就是说,等待某事失败并处理它比完全避免失败更容易。这种情况下,既然别无选择,那就去吧。

【讨论】:

  • 还有一个替代方案 - 编写一个 BigDecimal 验证器,如果传递的小数无效,则返回 false。另见鲍里斯的答复。通常,检查/应该/是if (x == valid) doSomething,并且应该避免在catch块中发现一些逻辑(如返回false)的格式try x catch y
  • 总是有不止一种方法可以做到这一点。
  • @Erick:好吧,我想我得到了我所得到的,因为我假装喜欢 Python 比我真正喜欢的多五分钟,尽管我不知道你认为哪里有任何消极情绪,然而,它可能会出现“面纱”。 @Cthulhu:当我发布我的答案时,我对 OP 的印象是没有更简单的选择。 @Dean:没错。
【解决方案3】:

如果您不喜欢这样的方法,请尝试使用来自Apache Commons ValidatorBigDecimalValidator。如果输入无效String,则返回null

【讨论】:

  • 如果性能是一个问题,我会倾向于 Apache Commons;实例化异常并抛出它比简单地检查某些东西是否有效需要更长的时间。
  • 即使不考虑性能损失,我也避免使用 try/catch 进行逻辑测试,因为它会混淆调试器。我不知道它在您最喜欢的 Java 调试器中的情况如何,但在 Visual Studio 中,我通常以调试器因任何异常而中断的模式运行。在“正常”情况下抛出并期望作为程序逻辑的一部分被捕获的异常变得令人讨厌。太糟糕了,微软在 .NET 框架实现的各个部分都没有遵循这个建议。
【解决方案4】:

性能可能不是很好,语法也很冗长,但代码对它的作用非常精确。检查和使用之间没有重复,这始终是一个大问题。

(注意,这种与字符串的特殊类型转换实际上是用于调试和内部配置。它不处理语言环境和其他面向人的考虑。在文件格式和有线协议中使用会引入对表示的强依赖性类使用。)

【讨论】:

  • 其实前置条件(检查输入是否有效,然后执行)和异常处理(输入无效时执行,捕获异常)的性能差异几乎是相等的,尤其是在大多数输入正确的情况下的案例。当然,这是值得商榷的。
  • @Cthulhu 如果每个案例都是一个快乐的案例,它可能会更快。至少在某些人尝试在您的服务器上执行 DoS 之前。 sad case 的性能很差。 虽然如果是真正的人类打字,这不太可能成为问题。
  • 汤姆:你试过了吗?上次有人问这个问题时,try/catch 案例实际上比 pre-validation 案例快。
  • @Gabe 我相信您将此与 JVM 生成的异常情况混淆了,这可能会被优化掉。对于库代码生成的异常,这不会发生(尽管理论上是可能的)。
  • 汤姆:无论如何,证明我错了。我记得有人想要验证 float 并且运行正则表达式来验证它比仅处理异常要慢。
【解决方案5】:

是的,这当然与实用程序员的“例外情况的例外”概念相冲突,但您知道自己在做什么,所以 IMO 没有问题

【讨论】:

  • Martin Fowler 也有一个名为“用测试替换异常”的重构,他提出了相同的论点。
【解决方案6】:

有两种已知的“检查”前置条件的方法。

LBYL : 跳之前先看看

这种编码风格会在调用或查找之前明确测试前置条件。这种风格与 EAFP 方法形成对比,其特点是存在许多 if 语句。

EAFP:请求宽恕比请求许可更容易。

这种常见的编码风格假设存在有效的键或属性,如果假设被证明是错误的,则会捕获异常。这种干净快速的风格的特点是存在许多 try 和 except 语句。该技术与许多其他语言(如 C)常见的 LBYL 风格形成鲜明对比。

EAFP 对于具有鸭子类型的语言总是一个好主意。

这显然取决于您想要做什么...如果您不确定要操作的对象类型,请使用 EAFP。

【讨论】:

    【解决方案7】:

    对 Kriss 回答的评论: 我在这里看不到“极好的内存泄漏”。没有对创建的 BigDecimal 的引用。一旦此方法完成,并且我们超出范围,该对象就有资格进行垃圾回收。

    当我们持有不再需要的引用时会发生内存泄漏,因此无法对对象进行垃圾回收。

    【讨论】:

      【解决方案8】:

      Try / Catch 块永远不应该用于逻辑。

      【讨论】:

      • 在谈到编程风格时,绝不应该如此强调和绝对地使用“从不”这个词。
      • @Tesserex 何时应将 Try / Catch 块用于逻辑?
      • @Tesserex 它与风格无关,与性能有关。如果您的应用程序不是那么大,您可以懒惰并使用异常来避免正确处理数据,那么就去做吧。
      • Rex 的评论是您的答案。这个问题的最高投票答案也是如此。
      【解决方案9】:

      当然,为什么不呢。这是我们检查客户指定的电子邮件地址是否格式正确的方法:

      try
      {
          MailMessage m = new MailMessage(from, to, subject, body);
          return true;
      }
      catch(SmtpFailedRecipientsException ex)
      {
          return false;
      }
      

      现在,人们可能会争论结构的性能或适当性,但他们忘记了简单性的权衡:

      • 以上代码将捕获 .NET 接受的 EXACT 数据格式。任何解析过电子邮件地址的人都会知道,关于什么是格式正确的电子邮件地址结构存在很大差异。此代码保证以 .NET 喜欢的方式验证电子邮件地址结构,而不是我认为应该的方式。
      • 该异常包含多个用例,而不仅仅是基本数据结构的正确性。它将验证 CC、BCC 和 TO 字段中的多个用户,这与手写代码相比开始变得笨拙。

      正如上面其他人所讨论的,最好将此代码抽象为一个单独的实用程序类,而不是与您的主代码混合。

      【讨论】:

        【解决方案10】:

        您必须考虑到 Exception 对象的构造在 JVM 的时间和资源方面是昂贵的,因为它必须构造 strack 跟踪。

        因此,您提出的解决问题的方法简单但耗费资源。

        所以这个解决方案是否可以接受取决于你要给这个功能的用途,以及你的效率要求。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2011-11-28
          • 2012-06-04
          • 2011-06-06
          • 1970-01-01
          • 2010-10-01
          • 1970-01-01
          • 2014-06-14
          相关资源
          最近更新 更多