陷阱 - 使用正常流程控制的异常

有一些 Java 专家不会背诵的口头禅:

“异常情况只应用于特殊情况。”

(例如: http//programmers.stackexchange.com/questions/184654

其实质是使用异常和异常处理来实现正常的流控制是一个坏主意(在 Java 中)。例如,比较这两种处理可能为 null 的参数的方法。

public String truncateWordOrNull(String word, int maxLength) {
    if (word == null) {
        return "";
    } else {
        return word.substring(0, Math.min(word.length(), maxLength));
    }
}

public String truncateWordOrNull(String word, int maxLength) {
    try {
        return word.substring(0, Math.min(word.length(), maxLength));
    } catch (NullPointerException ex) {
        return "";
    }
}

在这个例子中,我们(通过设计)处理 wordnull 的情况,好像它是一个空单词。这两个版本使用传统的 if … else 和或者 try … catch 来处理 null 。我们该如何决定哪个版本更好?

第一个标准是可读性。虽然可读性难以客观量化,但大多数程序员都同意第一版的基本含义更容易辨别。实际上,为了真正理解第二种形式,你需要明白 NullPointerExceptionString.substring 方法不能抛出 NullPointerException

第二个标准是效率。在 Java 8 之前的 Java 版本中,第二个版本比第一个版本慢很多(数量级)。特别是,异常对象的构造需要捕获和记录堆栈帧,以防万一需要堆栈跟踪。

另一方面,在许多情况下,使用异常比使用条件代码处理异常事件更具可读性,更高效和(有时)更正确。实际上,在极少数情况下有必要将它们用于非特殊事件; 即相对频繁发生的事件。对于后者,值得研究减少创建异常对象的开销的方法。