Java 断言

Java 中的断言通过测试我们认为是正确的代码来帮助检测错误。

断言是使用 assert 关键字进行的。

其语法为

assert condition;

在这里,condition 是一个布尔表达式,我们假设在程序执行时它是正确的。


启用断言

默认情况下,断言在运行时是禁用的并且被忽略。

要启用断言,我们使用

java -ea:arguments

java -enableassertions:arguments

当启用断言且条件为 true 时,程序正常执行。

但是,如果在启用断言时条件计算为 false,JVM 会抛出 AssertionError,程序会立即停止。


示例 1:Java 断言

class Main {
  public static void main(String args[]) {
    String[] weekends = {"Friday", "Saturday", "Sunday"};
    assert weekends.length == 2;
    System.out.println("There are " + weekends.length + "  weekends in a week");
  }
}

输出

There are 3 weekends in a week

我们得到上述输出是因为该程序没有编译错误,并且默认情况下禁用了断言。

启用断言后,我们得到以下输出

Exception in thread "main" java.lang.AssertionError

另一种形式的断言语句

assert condition : expression;

在这种形式的断言语句中,一个表达式被传递给 AssertionError 对象的构造函数。此表达式的值将在条件为 false 时作为错误详细消息显示。

详细消息用于捕获和传输断言失败的信息,以帮助调试问题。


示例 2:带有表达式的 Java 断言示例

class Main {
  public static void main(String args[]) {
    String[] weekends = {"Friday", "Saturday", "Sunday"};
    assert weekends.length==2 : "There are only 2 weekends in a week";
    System.out.println("There are " + weekends.length + "  weekends in a week");
  }
}

输出

Exception in thread "main" java.lang.AssertionError: There are only 2 weekends in a week

正如我们在上面的示例中看到的,表达式被传递给 AssertionError 对象的构造函数。如果我们的假设是 false 并且启用了断言,则会抛出一个带有适当消息的异常。

此消息有助于诊断和修复导致断言失败的错误。


为特定类和包启用断言

如果我们不向断言命令行开关提供任何参数,

java -ea

这会在除系统类之外的所有类中启用断言。

我们还可以使用参数为特定类和包启用断言。可以提供给这些命令行开关的参数是

在类名中启用断言

要为我们程序 Main 的所有类启用断言,

java -ea Main

要仅启用一个类,

java -ea:AnimalClass Main

这仅在 `Main` 程序中的 `AnimalClass` 中启用断言。

在包名中启用断言

仅为 `com.animal` 包及其子包启用断言,

java -ea:com.animal... Main

在未命名包中启用断言

要在当前工作目录中的未命名包(当我们不使用包语句时)中启用断言。

java -ea:... Main

在系统类中启用断言

要启用系统类中的断言,我们使用不同的命令行开关

java -esa:arguments 

java -enablesystemassertions:arguments

可以提供给这些开关的参数与启用它们时相同。


禁用断言

要禁用断言,我们使用

java -da arguments 

java -disableassertions arguments

要禁用系统类中的断言,我们使用

java -dsa:arguments

java -disablesystemassertions:arguments

禁用断言时可以传递的参数与启用它们时相同。


断言的优点

  1. 快速有效地检测和纠正错误。
  2. 断言检查仅在开发和测试期间进行。它们在生产代码的运行时自动删除,因此不会减慢程序执行速度。
  3. 它有助于删除样板代码并使代码更具可读性。
  4. 在更确信代码能正确运行的情况下重构和优化代码。

何时使用断言

1. 无法到达的代码

无法到达的代码是指在尝试运行程序时不会执行的代码。使用断言来确保无法到达的代码确实无法到达。

让我们举个例子。

void unreachableCodeMethod() {
  System.out.println("Reachable code");
  return;
  // Unreachable code
  System.out.println("Unreachable code");
  assert false;
}

让我们以另一个没有默认情况的 switch 语句示例。

switch (dayOfWeek) {
  case "Sunday":
    System.out.println("It’s Sunday!");
    break;
  case "Monday":
    System.out.println("It’s Monday!");
    break;
  case "Tuesday":
    System.out.println("It’s Tuesday!");
    break;
  case "Wednesday":
    System.out.println("It’s Wednesday!");
    break;
  case "Thursday":
    System.out.println("It’s Thursday!");
    break;
  case "Friday":
    System.out.println("It’s Friday!");
    break;
  case "Saturday":
    System.out.println("It’s Saturday!");
    break;
}

上面的 switch 语句表明一周中的天数只能是以上 7 个值之一。没有默认情况意味着程序员认为其中一种情况将始终执行。

但是,可能还有一些尚未考虑到的情况,其中假设实际上是错误的。

应该使用断言来检查此假设,以确保不会到达默认的 switch 情况。

default:
    assert false: dayofWeek + " is invalid day";

如果 dayOfWeek 的值不是有效日期,则会抛出 AssertionError


2. 记录假设

为了记录他们基本的假设,许多程序员使用注释。让我们举个例子。

if (i % 2 == 0) {
    ...
} else { // We know (i % 2 == 1)
    ...
}

而是使用断言。

随着程序的增长,注释可能会过时且不同步。但是,我们将被迫更新 assert 语句;否则,它们可能会因为有效条件而失败。

if (i % 2 == 0) {
   ...
} else {
    assert i % 2 == 1 : i;
    ...
}

何时不使用断言

1. 公共方法的参数检查

公共方法的参数可以由用户提供。

因此,如果使用断言来检查这些参数,条件可能会失败并导致 AssertionError

不要使用断言,而是让其导致适当的运行时异常并处理这些异常。


2. 评估会影响程序运行的表达式

不要在断言条件中调用会影响程序运行的方法或评估异常。

让我们以一个列表 weekdays 为例,其中包含一周中所有天的名称。


ArrayList<String> weekdays = new ArrayList<>(Arrays.asList("Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" ));

ArrayList<String> weekends= new ArrayList<>(Arrays.asList("Sunday", "Saturday" ));

assert weekdays.removeAll(weekends);

在这里,我们尝试从 ArrayList weekdays 中删除元素 `Saturday` 和 `Sunday`。

如果启用了断言,程序可以正常工作。但是,如果禁用了断言,则不会从列表中删除元素。这可能会导致程序失败。

相反,将结果分配给一个变量,然后使用该变量进行断言。

ArrayList<String> weekdays = new ArrayList<>(Arrays.asList("Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" ));

ArrayList<String> weekends= new ArrayList<>(Arrays.asList("Sunday", "Saturday" ));

boolean weekendsRemoved = weekdays.removeAll(weekends);
assert weekendsRemoved;

这样,我们可以确保所有 weekends 都从 weekdays 中删除,而不管断言是启用还是禁用。结果,它不会影响程序的未来运行。

你觉得这篇文章有帮助吗?

我们的高级学习平台,凭借十多年的经验和数千条反馈创建。

以前所未有的方式学习和提高您的编程技能。

试用 Programiz PRO
  • 交互式课程
  • 证书
  • AI 帮助
  • 2000+ 挑战