Java如果与try / catch开销

Java中有没有使用try / catch块的开销,而不是一个if块 (假设所附的代码否则不要求这样做)?

例如,对于string采取以下两个“安全修剪”方法的简单实现:

public String tryTrim(String raw) { try { return raw.trim(); } catch (Exception e) { } return null; } public String ifTrim(String raw) { if (raw == null) { return null; } return raw.trim(); } 

如果rawinput只是很less为null ,这两种方法之间是否存在性能差异

此外,使用tryTrim()方法来简化代码布局是一个很好的编程模式 ,特别是通过在一个try / catch块中封装代码,可以避免很多if块检查罕见的错误情况?

例如,通常情况下,有一个N parameters的方法,如果任何这样的参数是“无效的”(例如空string或空string),则在其起点附近使用M <= N ,快速失败并确定性地执行。而不影响其余的代码。

在这种情况下, 如果块k是每个参数的平均检查次数,例如k = 2表示空string或空string)不必写k * Mtry / catch块会显着缩短代码,1 -2行评论可以用来明确地注意到“非常规”的逻辑。

这样的模式也会加快方法的速度,尤其是在错误情况很less发生的情况下,并且这样做不会影响程序的安全性(假设错误条件是“正常的”,例如在string处理方法中,空值或空值是可以接受的,虽然很less在场)。

我知道你问的是性能开销,但你真的不应该使用try / catchif可以互换。

try / catch是出于你的控制之外的错误,而不是在正常的程序stream程中。 例如,试图写入文件并且文件系统已满? 这种情况通常应该用try / catch来处理。

if语句应该是正常的stream程和普通的错误检查。 那么,例如,用户无法填充所需的input字段? if是这样的话,不要try / catch

在我看来,你的示例代码强烈build议正确的方法是if语句而不是try / catch

为了回答你的问题,我会推测,在try / catch通常会有比if更多的开销。 要知道肯定,得到一个Java分析器,并找出你所关心的具体代码。 答案可能因情况而异。

这个问题几乎已经“应对死刑”了,但是我认为还有几点可以有用地提出:

  • 对于非例外控制stream使用try / catch是不好的风格(在Java中)。 (人们常常在争论什么是“非例外”,但这是一个不同的话题。)

  • 部分原因是不好的风格是try / catch比常规的控制stream程语句更昂贵1 。 实际的差异取决于程序和平台,但我预计它将是1000倍或更多的昂贵。 除此之外, 创buildexception对象捕获堆栈跟踪,查找和复制堆栈上每个帧的信息。 堆栈越深,需要复制的越多。

  • 另一个原因是风格不好的部分是代码难以阅读。

1 – 最近版本的Java 7中的JIT可以优化exception处理,以在某些情况下大幅降低开销。 但是,这些优化默认情况下不启用。

你写这个例子的方式也有问题:

  • 捕捉Exception是非常糟糕的做法,因为您有机会偶然发现其他未经检查的exception。 例如,如果你是通过调用raw.substring(1)来做到这一点,那么你也可以捕获潜在的StringIndexOutOfBoundsException s …并隐藏错误。

  • 你的例子试图做的是(可能)由于处理nullstring的不良做法的结果。 作为一般原则,您应该尽量减lessnullstring的使用,并尝试限制其(有意)的传播。 在可能的情况下,使用空string而不是null来表示“无值”。 当你有一个需要传递或返回一个nullstring的情况下,清楚地logging在你的方法javadocs中。 如果你的方法在不应该被调用时调用null ,那么这是个bug。 让它抛出一个exception。 不要试图弥补(在这个例子中)返回null的错误。


跟进

我的问题是更一般的,不仅是空值。

…我的答案中的大部分要点都不是关于空值的!

但请记住,有很多情况下,您希望允许偶尔出现的空值或可能产生exception的任何其他值,而忽略它们。 例如,当从某处读取键/值的值并将其传递给像上面的tryTrim()这样的方法时就是这种情况。

是的,有些情况下预计会出现null值,您需要处理它们。

但是我会认为tryTrim()所做的是(通常)处理null的错误方法。 比较这三位代码:

 // Version 1 String param = httpRequest.getParameter("foo"); String trimmed = tryTrim(param); if (trimmed == null) { // deal with case of 'foo' parameter absent } else { // deal with case of 'foo' parameter present } // Version 2 String param = httpRequest.getParameter("foo"); if (param == null) { // deal with case of 'foo' parameter absent } else { String trimmed = param.trim(); // deal with case of 'foo' parameter present } // Version 3 String param = httpRequest.getParameter("foo"); if (param == null) { // treat missing and empty parameters the same param = ""; } String trimmed = param.trim(); 

最后,你必须处理null不同于一个普通的string, 通常是一个好主意尽快做到这一点。 允许null从其原点传播得越远,程序员就越有可能忘记 null值是可能性,并写出假设非空值的错误代码。 忘记HTTP请求参数可能会丢失(即param == null )是发生这种情况的经典案例。

我并不是说tryTrim()本质上是不好的。 但是程序员认为需要这样写的方法这个事实可能表明不够理想的空值处理。

使用第二个版本。 当其他select可用时,不要使用控制stream的exception,因为这不是它们的原因。 例外情况是特殊情况。

而在这个话题上,不要在这里捕捉Exception ,特别是不要吞下它。 在你的情况下,你会期望一个NullPointerException 。 如果你要抓住一些东西,那就是你会抓到的( 但是回到第一段,不要这么做 )。 当你抓住(吞下!) Exception ,你就是在说:“无论出什么事,我都能处理,我不在乎它是什么。” 你的程序可能处于不可撤销的状态! 只有抓住你准备处理的事情,让其他所有的东西传播到一个可以处理它的层,即使这个层是最上层,它所做的只是loggingexception,然后点击popup开关。

否则,抛出和捕获的exception很快(尽pipeif可能还是更快),但是缓慢的事情是创buildexception的堆栈跟踪,因为它需要遍历所有当前堆栈。 (一般来说,对控制stream使用exception是不好的,但是当真正需要exception并且exception必须快时,可以通过重写Throwable.fillInStackTrace()方法来跳过构build堆栈跟踪,或者保存一个exception实例和重复抛出它而不是总是创build一个新的exception实例。)

就开销而言,你可以为自己testing:

 public class Overhead { public static void main(String[] args) { String testString = ""; long startTimeTry = System.nanoTime(); tryTrim(testString); long estimatedTimeTry = System.nanoTime() - startTimeTry; long startTimeIf = System.nanoTime(); ifTrim(testString); long estimatedTimeIf = System.nanoTime() - startTimeIf; System.out.println("Try time:" + estimatedTimeTry); System.out.println("If time:" + estimatedTimeIf); } public static String tryTrim(String raw) { try { return raw.trim(); } catch (Exception e) { } return null; } public static String ifTrim(String raw) { if (raw == null) { return null; } return raw.trim(); } 

}

我得到的数字是:

 Try time:8102 If time:1956 

至于什么风格 – 这是一个单独的问题。 if语句看起来很自然,但是这个try看起来很奇怪,原因很多: – 即使你正在检查NULL值,你是否抓到了Exception,你是否期待发生一些“例外”(否则捕获NullException)? – 你发现这个例外,你要报告还是吞咽? 等等等等

编辑:看看我的评论为什么这是一个无效的testing,但我真的不想离开这个站在这里。 通过交换tryTrim和ifTrim,我们突然得到以下结果(在我的机器上):

 Try time:2043 If time:6810 

而不是开始解释所有这一切,只是从头开始阅读 – 关于整个话题,克利夫也有一些很棒的幻灯片,但是我现在找不到链接。

知道exception处理是如何在Hotspot中工作的,我相当肯定,在一个正确的testing中,try / catch没有exception)是基线性能(因为没有任何开销),但是JIT可以使用Nullpointer检查,方法调用(没有明确的检查,但如果对象为空则捕获exception),在这种情况下,我们会得到相同的结果。 另外不要忘记:我们正在谈论一个容易预测的差异,那就是一个CPU周期! 修剪电话将花费一百万次。