58.md 7.7 KB
Newer Older
W
wizardforcel 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
# Java 断言

> 原文: [https://www.programiz.com/java-programming/assertions](https://www.programiz.com/java-programming/assertions)

#### 在本教程中,我们将借助示例学习 Java 断言(Java 断言)。

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

使用`assert`关键字进行断言。

其语法为:

```java
assert condition;
```

W
wizardforcel 已提交
17
在这里,`condition`是一个布尔表达式,我们假定在程序执行时为`true`
W
wizardforcel 已提交
18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76

* * *

## 启用断言

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

为了启用断言,我们使用:

```java
java -ea:arguments
```

要么

```java
java -enableassertions:arguments
```

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

但是,如果在启用断言的情况下条件求值为`false`,则 JVM 会抛出`AssertionError`,程序将立即停止。

* * *

### 示例 1:Java 断言

```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");
  }
} 
```

**输出**

```java
There are 3 weekends in a week 
```

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

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

```java
Exception in thread "main" java.lang.AssertionError 
```

* * *

## 断言声明的另一种形式

```java
assert condition : expression; 
```

W
wizardforcel 已提交
77
在这种形式的断言语句中,将表达式传递到`AssertionError`对象的构造器。 如果条件为`false`,则此表达式的值显示为错误的详细信息。
W
wizardforcel 已提交
78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94

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

* * *

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

```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");
  }
} 
```

W
wizardforcel 已提交
95
**输出**
W
wizardforcel 已提交
96 97 98 99 100

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

W
wizardforcel 已提交
101
从上面的示例中可以看到,表达式被传递给`AssertionError`对象的构造器。 如果我们的假设是`false`并且启用了断言,则会抛出异常并附带一条适当的消息。
W
wizardforcel 已提交
102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119

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

* * *

## 对特定的类和包启用断言

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

```java
java -ea

```

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

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

W
wizardforcel 已提交
120
**启用类名中的断言**
W
wizardforcel 已提交
121

W
wizardforcel 已提交
122
为了对程序`Main`的所有类启用断言,
W
wizardforcel 已提交
123 124 125 126 127

```java
java -ea Main
```

W
wizardforcel 已提交
128
要仅启用一个类,
W
wizardforcel 已提交
129 130 131 132 133 134 135

```java
java -ea:AnimalClass Main
```

这仅允许在`Main`程序的`AnimalClass`中声明。

W
wizardforcel 已提交
136
**启用包名称中的断言**
W
wizardforcel 已提交
137

W
wizardforcel 已提交
138
要仅启用包`com.animal`及其子包的断言,
W
wizardforcel 已提交
139 140 141 142 143

```java
java -ea:com.animal... Main
```

W
wizardforcel 已提交
144
**启用未命名包中的断言**
W
wizardforcel 已提交
145

W
wizardforcel 已提交
146
要在当前工作目录中的未命名包中启用断言(当我们不使用`package`语句时)。
W
wizardforcel 已提交
147 148 149 150 151

```java
java -ea:... Main
```

W
wizardforcel 已提交
152
**启用系统类中的断言**
W
wizardforcel 已提交
153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226

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

```java
java -esa:arguments 
```

OR

```java
java -enablesystemassertions:arguments
```

可以提供给这些开关的参数是相同的。

* * *

## 禁用断言

要禁用断言,我们使用:

```java
java -da arguments 
```

OR

```java
java -disableassertions arguments 
```

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

```java
java -dsa:arguments
```

OR

```java
java -disablesystemassertions:arguments
```

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

* * *

## 断言的优点

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

* * *

## 何时使用断言

### 1.无法访问的代码

无法访问的代码是当我们尝试运行该程序时不会执行的代码。 使用断言来确保无法访问的代码实际上是不可访问的。

让我们举个例子。

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

W
wizardforcel 已提交
227
让我们再举一个不带默认大小写的`switch`语句示例。
W
wizardforcel 已提交
228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254

```java
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;
} 
```

W
wizardforcel 已提交
255
上面的`switch`语句指示星期几只能是上述 7 个值之一。 没有默认情况意味着程序员认为这些情况之一将始终被执行。
W
wizardforcel 已提交
256 257 258 259 260 261 262 263 264 265

但是,在某些假设实际上是错误的情况下,可能尚未考虑某些情况。

应该使用断言来检查此假设,以确保未达到默认切换条件。

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

W
wizardforcel 已提交
266
如果`dayOfWeek`的值不是有效天数,则会抛出`AssertionError`
W
wizardforcel 已提交
267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310

* * *

### 2.记录假设

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

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

请改用断言。

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

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

* * *

## 什么时候不使用断言

### 1.公共方法中的参数检查

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

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

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

* * *

### 2.计算影响程序运行的表达式

W
wizardforcel 已提交
311
不要调用方法或求值可能在断言条件下影响程序操作的异常。
W
wizardforcel 已提交
312

W
wizardforcel 已提交
313
让我们以列表`weekdays`为例,该列表包含一周中所有天的名称。
W
wizardforcel 已提交
314 315 316 317 318 319 320 321 322

```java
 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); 
```

W
wizardforcel 已提交
323
在这里,我们试图从工作日的`ArrayList`中删除元素`Saturday``Sunday`
W
wizardforcel 已提交
324 325 326 327 328 329 330 331 332 333 334 335 336 337

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

而是将结果分配给变量,然后使用该变量进行断言。

```java
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; 
```

W
wizardforcel 已提交
338
这样,无论断言是启用还是禁用,我们都可以确保从`weekdays`中删除所有`weekends`。 结果,它不会影响将来的程序操作。