Java if vs. try/catch 开销

Java中是否有任何开销用于使用 try/catch 块,而不是 if 块(假设包含的代码不请求这样做)?

例如,采用字符串的“安全修剪”方法的以下两个简单实现:

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();
}

如果输入很少,两种方法之间有什么性能差异吗?rawnull

此外,使用该方法简化代码布局是否是一种很好的编程模式,特别是当许多如果块检查罕见的错误条件可以通过将代码包含在一个 try/catch 块中来避免这种情况时?tryTrim()

例如,通常的情况是使用 的方法,该方法在其开头附近使用它们,如果任何此类参数“无效”(例如,null或空字符串),则快速且确定地失败,而不会影响代码的其余部分。N parametersM <= N

在这种情况下,不必编写 if 块(其中是每个参数的平均检查次数,例如 对于 null 或空字符串),try/catch 块将显著缩短代码,并且可以使用 1-2 行注释来显式注释“非常规”逻辑。k * Mkk = 2

这样的模式也会加快方法的速度,特别是如果错误条件很少发生,并且它会在不损害程序安全性的情况下这样做(假设错误条件是“正常的”,例如,在字符串处理方法中,null或空值是可以接受的,尽管很少存在)。


答案 1

我知道您询问的是性能开销,但您真的不应该使用 /和互换。trycatchif

try/catch适用于超出您控制范围且不在正常程序流中出错的事物。例如,尝试写入文件,而文件系统已满?这种情况通常应该用 / 来处理。trycatch

if语句应该是正常流和普通错误检查。那么,例如,用户无法填充所需的输入字段?用于此,而不是 /。iftrycatch

在我看来,您的示例代码强烈建议正确的方法是使用语句而不是 /。iftrycatch

为了回答您的问题,我推测a /中的开销通常比.要确定,请获取 Java 分析器并找出您关心的特定代码。答案可能因情况而异。trycatchif


答案 2

这个问题几乎被“回答得死去活来”,但我认为还有几点可以有用地提出:

  • 用于非异常控制流是不好的风格(在Java中)。(关于“非例外”的含义经常存在争议......但这是一个不同的话题。try / catch

  • 它是不良样式的部分原因是它比常规控制流语句1几个数量级。实际差异取决于程序和平台,但我预计它的价格会高出1000倍或更多。除此之外,创建异常对象会捕获堆栈跟踪,查找并复制有关堆栈上每个帧的信息。堆栈越深,需要复制的内容就越多。try / catch

  • 它是不良风格的另一部分是代码更难阅读。

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

您编写示例的方式也存在问题:

  • 捕获是非常糟糕的做法,因为您有可能意外捕获其他未经检查的异常。例如,如果你在打电话给你的时候这样做,你也会抓住潜在的......并隐藏错误。Exceptionraw.substring(1)StringIndexOutOfBoundsException

  • 您的示例试图做的(可能)是处理字符串时实践不佳的结果。作为一般原则,您应该尝试尽量减少字符串的使用,并尝试限制它们的(有意)传播。如果可能,请使用空字符串而不是表示“无值”。当您确实需要传递或返回字符串时,请在方法javadocs中清楚地记录它。如果你的方法被调用与 a 当它们不应该 ...这是一个错误。让它引发异常。不要试图通过(在此示例中)返回 来补偿错误。nullnullnullnullnullnull


我的问题更笼统,而不仅仅是价值观。null

...我的答案中的大多数观点都不是关于价值观的!null

但请记住,在许多情况下,您希望允许偶尔的 null 值或任何其他可能产生异常的值,而忽略它们。例如,当从某个地方读取键/对值并将其传递给像上面的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();

最终,您必须处理与常规字符串不同的问题,并且通常最好尽快执行此操作。允许从其点原点传播得越远,程序员就越有可能忘记某个值是一种可能性,并编写假定非空值的错误代码。忘记HTTP请求参数可能丢失(即)是发生这种情况的经典情况。nullnullnullparam == null

我并不是说这本质上是坏事。但是,您觉得需要编写这样的方法这一事实可能表明 null 处理不太理想。tryTrim()

最后,还有其他方法可以对API中的“没有值”进行建模。这些包括:

  • 在 setter 和构造函数中测试意外情况,并引发异常或替换其他值。null
  • 添加方法...如果结果为 .,则引发异常。is<Field>Setget<Field>null
  • 使用空对象模式
  • 使用空字符串、空集合、零长度数组等代替字符串、集合或数组1null
  • 用。Optional

1 - 请注意,这与 Null 对象模式不同,因为不一定只有一个类型的实例来表示“nullity”。但是您可以将这两个想法结合起来...如果你对此有纪律。


推荐