随遇而安

随遇而安 关注TA

额,假装这里有签名...

随遇而安

随遇而安

关注TA

额,假装这里有签名...

  •  某地区
  • 工程师
  • 写了837,964字

最近回复

该文章投稿至Nemo社区   Java  板块 复制链接


Java异常有多慢?(转载)

发布于 2018/01/26 17:25 804浏览 0回复 3,999

本文作者:ImportNew - 曹旭东


本文是回答StackOverflow上的问题,但因为写太长了,所以就发到这里了。

实际上,真正要讨论的问题并不是,“相对‘那些不会发生错误的代码’来说,‘那些以异常形式上报的错误’会有多慢?”,因为你可能也认同“已接受的回答”。相反,真正的问题是,“相对‘那些以其他形式上报的错误’来说,‘那些以异常形式上报的错误’会有多慢?”

通常认为,“不要抛出你想要捕获的异常”。所以,抛出一个其他人——如平台或框架API——要捕获的异常是合适的。或者在编写一些工具API时,抛出异常也可以的,如日志记录或消息发送,这些操作需要处理外部虚拟机的错误,例如文件IO或网络IO错误。

这是适合抛出异常的例子,应该没有人会在这些例子上有争议。现在,看一下简单方法中出现错误时会发生什么。假设方法签名如下:

/**
 * Transforms SomeClass into SomeOtherClass.
 * @param input some class instance
 * @return the transformed instance,
 *         or null if the transformation was unsuccessful
 */
public SomeOtherClass transform(SomeClass input)

调用该方法的代码如下所示:

SomeClass input = ... // get the input from somewhere
SomeOtherClass result = transform(input);
if(result == null) {
   // handle the failure
} else {
   // use the result
}

但现在,当方法返回null时,我们想知道哪里出现错误了。简单来说可以这样:

/**
 * Transforms SomeClass into SomeOtherClass.
 * @param input some class instance
 * @return the transformed instance, never null
 * @throws TransformationException on failure
 */
public SomeOtherClass transform(SomeClass input) throws TransformationException

为了实现这个功能,你需要将“return null”改为“return new TransformationException(“reason”);”。调用方法需要做改动么?

SomeClass input = ... // get the input from somewhere
try {
   SomeOtherClass result = transform(input);
   // use the result
} catch(TransformationException e) {
   // handle the failure
}

没有人会去读上面的代码块,没什么意义。所以也没什么可惊讶的。你可能每天都在写类似的代码,但也说不上是“代码异味”。可是,将来你会明白在“已预料到”的错误上使用异常是多么大的错误假设有一天你开始读到在“已预料到”的错误上使用异常是非常不好的。现在,捕获“未预料到”异常是非常可笑的,因为编写catch代码块,并显式的处理异常本身就是预料到会有异常。但没关系,我们还可以修改代码改变这种窘境。于是,我们退回到了C语言时代,返回一个异常值来表示错误。

/**
 * Transforms SomeClass into SomeOtherClass.
 * @param input some class instance
 * @return the SomeOtherClass instance or,
 *    if the transform fails, a TransformFailure instance
 */
public Object transform(SomeClass input)

于是,调用部分的代码也不得不奇葩一些:

SomeClass input = ... // get the input from somewhere
Object result = transform(input);
if(result instanceof SomeOtherClass) {
   SomeOtherClass success = (SomeOtherClass)result;
   // use the result
} else {
   TransformFailure failure = (TransformFailure)result;
   // handle the failure
}

所以,如果你觉着使用异常有代码异味,但可以接受打破类型安全,那么你最终要面对的是难以维护,没法使用,仅仅比基于异常的解决方案快两倍的代码。但是你又不能接受类型安全被破坏,因为这2倍的性能提升还未被证明,现在就用实在太鲁莽。所以,你决定使用结果对象,而不是返回异常值。

/**
  * Transforms SomeClass into SomeOtherClass.
  * @param input some class instance
  * @return the TransformResult instance
  */
 public TransformResult transform(SomeClass input)

现在,相应地,调用部分的代码变成了这样:

SomeClass input = ... // get the input from somewhere
TransformResult result = transform(input);
if(result.isSuccess()) {
   SomeOtherClass success = result.getSuccess();
   // use the result
} else {
   TransformFailure failure = result.getFailure();
   // handle the failure
}

现在,我们有了一个隐晦,但可管理的解决方案。可是,它比使用异常的第一个方案慢2倍,即使你将TransformResult和TransformFailure合并也是一样的。再说明一遍,使用结果对象比使用异常慢,即使在调用过程中发生了错误。每次你都需要创建一个新的结果对象,这没什么实际意义,而异常对象只在发生错误的时候才会创建。

对于异常,还有一个要讨论的地方。假设有人在使用方法transform时,没有认真看javadoc。在使用异常的例子中,会有下面的代码:

SomeClass input = ... // get the input from somewhere
SomeOtherClass result = transform(input);
// use the result

在使用异常的例子中,他们知道返回值的类型,以及是否一个“已检查异常”,他们可能会得到一个编译时错误,或者他们会在throws语句中声明相应的异常。即使是“未检查异常”,错误会传递到上层调用。现在,考虑使用异常返回值的例子:

SomeClass input = ... // get the input from somewhere
SomeOtherClass result = (SomeOtherClass)transform(input);
// use the result

这个粗心的用户写的代码看起来挺漂亮,但当运行过程中发生错误时,就满不是那么回事了。那时,你费尽力气提供的错误信息会因为发生了ClassCastException异常为全部丢失。使用结果对象也不会好到哪去。

SomeClass input = ... // get the input from somewhere
SomeOtherClass result = transform(input).getSuccess();
// use the result

再说一遍,上面的代码看来相当正常。如果他们盲目使用本文中给出的第一个方法,那么在程序运行过程中,肯定会出现NullPointerException异常。

这里主要想说的是,处理逻辑错误时,使用异常的例子可以按预想的方式正常工作,报告错误信息。但是其他的解决方案却会产生一些没用的异常,即使你已经正确将软件重新部署了一遍,它仍然会出错,只有这时,你才能得到错误信息。

所以,唯一符合逻辑性的结论是,如果你想上报错误信息,那么就应该使用异常。它比结果对象性能高,比异常返回值安全,而且运行稳定。

 

更新于2012-12-19 19:23

感谢pony指出的译文中的错误。

 

 英文原文:How slow are Java Exception,翻译:ImportNew - 曹旭东

译文链接:http://www.importnew.com/1480.html

点赞(0)
本文标签
 java  异常
点了个评