关于java:什么时候可以对业务逻辑使用异常处理?

When is it OK to use exception handling for business logic?

我认为,作为Java中的一个通用规则(也许任何语言都有异常处理),人们应该尽量避免使用异常处理来真正处理业务逻辑。一般来说,如果预期会发生某种情况,那么应该检查并更直接地处理它,而不是依赖异常处理来为您进行检查。例如,以下不被视为良好做法:

1
2
3
4
5
try{
  _map.put(myKey, myValue);
} catch(NullPointerException e){
  _map = new HashMap<String, String>();
}

相反,惰性初始化应该更像这样完成:

1
2
3
4
if(_map == null){
  _map = new HashMap<String, String>();
}
_map.put(myKey, myValue);

当然,逻辑可能远比简单地处理延迟初始化复杂得多。因此,考虑到这种类型的事情通常是不受欢迎的……如果有的话,当某个业务逻辑发生时,依赖发生的异常是一个好主意吗?是否准确地说,任何一个被迫使用这种方法的实例实际上都突出了正在使用的API的弱点?


无论何时,只要可以预料到例外,但不能避免。

例如,如果您依赖某种类型的外部API来解析数据,并且该API提供了解析方法,但是没有任何内容来说明给定的输入是否可以被解析(或者解析是否成功取决于您无法控制的因素,但是API没有提供适当的函数调用),并且解析方法抛出了异常当无法分析输入时打开。

有了一个设计得当的API,这应该归结为一个在"几乎从不"到"从不"范围内的数量。

我完全没有理由在代码中使用异常处理作为正常的流控制手段。它很昂贵,很难阅读(看看你自己的第一个例子,我知道它可能写得很快,但是当_map还没有初始化时,你最终得到的是一个空映射,丢弃了你试图添加的条目),它将代码中大量无用的尝试捕获块丢弃,这可以很好地隐藏真实的P罗伯茨再举个例子,如果对_map.add()的调用是出于除_mapnull以外的某种原因而抛出NullPointerException呢?突然间,您将静默地重新创建一个空映射,而不是向其中添加一个条目。我敢肯定,我不必说这会导致代码中完全不相关的地方出现任何数量的错误,因为出现了意想不到的状态…

编辑:只要清楚,上面的答案是用Java编写的。其他语言(显然也有)在例外的实现开销上可能有所不同,但其他观点仍然适用。


抛出异常是C++中相对昂贵的操作,也是Java中非常昂贵的操作。仅仅从效率的角度来看,避免一个明确的检查并接受一个例外是没有意义的。我想,在一些罕见的情况下,您可能能够证明这一点,在这种情况下,检查是否抛出异常非常复杂或几乎不可能,但否则,我会说答案是"几乎从未"。


这确实是一个讨论问题,答案取决于系统的设计。

我的直觉永远是一张毯子,从来没有,但我见过一些系统使用异常实现业务错误。我个人觉得这很恶心,但是我真的理解一旦你知道一个业务流程失败,我就可以立即中断它,处理你的失败(例如,回滚你的工作单元),并将错误返回给调用者,也许还需要添加信息。

一个可能的缺点是,跨几个不同的类进行错误处理是非常简单的,因此当进程失败时发生的定义很难从代码中推断出来。

不管怎样,这里没有单一的答案,您需要权衡这两种方法,有时还需要将它们结合起来。

关于您的示例,我看不到在流控制中使用异常的任何优势,特别是在"良好"(设计用于工作)场景中。


例外是对象是有原因的。还有一个原因是Java语言的设计者把所有EDCOX1,5个s分成2个主要类型:检查和未检查。

is it a good idea to rely on an
exception happening for certain
business logic to occur?

对。当然。你应该阅读"有效Java,第二版"的第9章。就在那里。解释得很好,在等你。


如果您处理的错误条件实际上是业务逻辑的一部分,那么可以使用异常。例如:

1
2
3
4
5
6
7
8
try{
   // register new user
   if(getUser(name) != null)
      throw new MyAppException("Such user already exists");
   //other registration steps......
}catch(MyAppException ex){
   sendMessage(ex.getMessage());
}