HOWTO · Java

Java 中的 assert 关键字

本文介绍何时以及如何使用 Java assert 关键字。

本页内容

Java 的 assert 关键字用于检查程序员的假设,而 JVM 默认会关闭断言。启动程序时添加 -ea 即可启用断言。启用断言后,如果条件计算结果为 false,Java 会抛出 AssertionError;否则程序会继续执行。断言适用于检查开发期间应当成立的内部假设,不应依赖断言来执行每次运行都必须完成的应用行为。

在 Java 中将 assert 用作布尔条件

基本形式是 assert condition;,其中 condition 必须是布尔表达式。启用断言后,如果表达式为 false,Java 会在断言语句处抛出 AssertionError。关闭断言时,JVM 会跳过该断言,也不会计算其中的条件。因此,条件不能执行程序依赖的工作。

下面这个完整的类会检查一个计数,并打印简短结果。

public class JavaAssertCheck {
  public static void main(String[] args) {
    String[] names = {"John", "Mary", "David"};
    assert names.length == 3;
    System.out.println("Name count: " + names.length);
  }
}

编译此类,然后使用 java -ea JavaAssertCheck 运行,以启用检查。不添加 -ea 运行时,断言会被跳过。如果将预期计数改为与数组长度不符的值并启用断言,程序会在断言处停止并抛出 AssertionError。

使用 assert condition : expression 添加诊断消息

如果希望失败时附带上下文,可以使用 assert condition : expression。只有在启用了断言且条件为 false 时,才会计算该表达式;其值会成为所抛出 AssertionError 的详细消息。例如,assert names.length == expected : "Expected " + expected + " names, got " + names.length; 会指出这两个计数。表达式应避免副作用,因为条件通过或断言关闭时都不会计算它。

使用 assert 标记意外分支

如果程序逻辑认为某个运行时分支不应到达,可以用断言标记该分支。Java 会拒绝放在无条件 return 之后的语句,因此应将断言放进编译器无法证明不可达的分支中:

public class StatusLabel {
  static String label(int status) {
    if (status == 0) {
      return "ready";
    } else if (status == 1) {
      return "busy";
    } else {
      assert false : "Unexpected status: " + status;
      return "unknown";
    }
  }

  public static void main(String[] args) {
    System.out.println(label(0));
  }
}

启用断言时,如果执行到最后一个分支,就会抛出带有状态信息的 AssertionError。关闭断言时,程序会执行后备的 return,因此这里的断言只是诊断辅助,不能作为必需的控制流程。

安全地使用断言检查内部不变量

断言适合记录由程序自身建立的不变量,例如内部计算后某个值仍处于指定范围内。开发期间,如果这类假设被违反,断言有助于更容易定位问题。

不要用断言验证传入公共方法的参数:无论 JVM 选项如何,调用方都需要得到明确的验证结果。同样,不要把必需的应用工作放进断言中,因为关闭断言时其条件不会执行。基于同样的原因,断言表达式也应避免副作用。只要程序在生产环境中必须始终以一致方式响应,就应使用常规验证和控制流程。