Java assert关键字做什么,什么时候应该使用?
要理解断言的关键作用,有哪些真实示例?来自堆栈类的真实示例(来自)Java assert关键字做什么,什么时候应该使用?,java,assertions,Java,Assertions,要理解断言的关键作用,有哪些真实示例?来自堆栈类的真实示例(来自) 断言用于检查post条件和“应永不失败”前提条件。正确的代码决不能使断言失败;当它们触发时,它们应该指示一个bug(希望在一个接近问题实际轨迹的地方) 断言的一个示例可能是检查是否以正确的顺序调用了一组特定的方法(例如,在迭代器中hasNext()在next()之前调用了hasNext())。(通过assert关键字)在Java1.4中添加。它们用于验证代码中不变量的正确性。它们不应在生产代码中触发,并且表示存在错误或代码路径被
断言用于检查post条件和“应永不失败”前提条件。正确的代码决不能使断言失败;当它们触发时,它们应该指示一个bug(希望在一个接近问题实际轨迹的地方) 断言的一个示例可能是检查是否以正确的顺序调用了一组特定的方法(例如,在
迭代器中hasNext()
在next()
之前调用了hasNext()
)。(通过assert关键字)在Java1.4中添加。它们用于验证代码中不变量的正确性。它们不应在生产代码中触发,并且表示存在错误或代码路径被误用。它们可以在运行时通过java
命令上的-ea
选项激活,但默认情况下不启用
例如:
public Foo acquireFoo(int id) {
Foo result = null;
if (id > 50) {
result = fooService.read(id);
} else {
result = new Foo(id);
}
assert result != null;
return result;
}
断言基本上用于调试应用程序,或者用于替换某些应用程序的异常处理,以检查应用程序的有效性
断言在运行时工作。这里有一个简单的例子,可以非常简单地解释整个概念—(WikiAnswers)。assert
是一个关键字。它是在JDK1.4中引入的。有两种类型的assert
s
非常简单的assert
语句
简单的assert
语句
默认情况下,不会执行所有assert
语句。如果断言语句接收到false,那么它将自动引发断言错误。假设您应该编写一个程序来控制核电站。很明显,即使是最微小的错误也可能导致灾难性的结果,因此您的代码必须是无bug的(为了参数的缘故,假设JVM是无bug的)
Java不是一种可验证的语言,这意味着:您无法计算操作的结果是否完美。这样做的主要原因是指针:指针可以指向任何地方,也可以不指向任何地方,因此无法计算出精确的值,至少不能在合理的代码范围内。考虑到这个问题,无法证明您的代码总体上是正确的。但你能做的是证明你至少在每一个bug发生时都能找到它
这个想法是基于(DbC)范式的:您首先定义(以数学精度)您的方法应该做什么,然后通过在实际执行过程中测试来验证这一点。例如:
// Calculates the sum of a (int) + b (int) and returns the result (int).
int sum(int a, int b) {
return a + b;
}
虽然这很明显可以很好地工作,但大多数程序员不会看到其中隐藏的错误(提示:Ariane V崩溃是因为类似的错误)。现在DbC定义您必须始终检查函数的输入和输出,以验证它是否正常工作。Java可以通过断言实现这一点:
// Calculates the sum of a (int) + b (int) and returns the result (int).
int sum(int a, int b) {
assert (Integer.MAX_VALUE - a >= b) : "Value of " + a + " + " + b + " is too large to add.";
final int result = a + b;
assert (result - a == b) : "Sum of " + a + " + " + b + " returned wrong sum " + result;
return result;
}
如果此功能现在失败,您将注意到它。您将知道代码中存在问题,您知道它在哪里,并且您知道是什么导致了它(类似于异常)。更重要的是:当它发生时,您停止正确执行,以防止任何进一步的代码使用错误的值工作,并可能对其控制的任何内容造成损害
Java异常是一个类似的概念,但它们无法验证所有内容。如果需要更多的检查(以牺牲执行速度为代价),则需要使用断言。这样做会使代码膨胀,但最终您可以以惊人的短开发时间交付产品(越早修复bug,成本越低)。此外:如果代码中有任何bug,您将检测到它。没有办法让一个bug溜走并在以后引起问题
这仍然不能保证代码没有bug,但它比普通程序更接近于这一点。这里是最常见的用例。假设您正在打开枚举值:
switch (fruit) {
case apple:
// do something
break;
case pear:
// do something
break;
case banana:
// do something
break;
}
只要你处理好每一个案子,你就没事。但有一天,有人会将fig添加到您的枚举中,而忘记将其添加到您的switch语句中。这会产生一个可能很难捕捉到的bug,因为只有在您离开switch语句之后才会感觉到效果。但是,如果您这样编写开关,您可以立即捕获它:
switch (fruit) {
case apple:
// do something
break;
case pear:
// do something
break;
case banana:
// do something
break;
default:
assert false : "Missing enum value: " + fruit;
}
断言是开发阶段捕获代码中错误的工具。它们被设计为易于删除,因此它们不会存在于生产代码中。因此,断言不是您交付给客户的“解决方案”的一部分。它们是内部检查,以确保您所做的假设是正确的。最常见的例子是测试null。许多方法都是这样编写的:
void doSomething(Widget widget) {
if (widget != null) {
widget.someMethod(); // ...
... // do more stuff with this widget
}
}
/**
* @param Widget widget Should never be null
*/
void doSomething(Widget widget) {
assert widget != null;
widget.someMethod(); // ...
... // do more stuff with this widget
}
assert value == null || value.equals("Y") || value.equals("N") : value;
...
if (i == 1) {
...
}
else if (i == 2) {
...
} else {
assert false : "cannot happen. i is " + i;
}
...
在这样的方法中,小部件通常不应该为null。因此,如果它为null,那么代码中的某个地方就有一个bug需要跟踪。但是上面的代码永远不会告诉你这一点。因此,为了编写“安全”代码,您还隐藏了一个bug。编写这样的代码要好得多:
void doSomething(Widget widget) {
if (widget != null) {
widget.someMethod(); // ...
... // do more stuff with this widget
}
}
/**
* @param Widget widget Should never be null
*/
void doSomething(Widget widget) {
assert widget != null;
widget.someMethod(); // ...
... // do more stuff with this widget
}
assert value == null || value.equals("Y") || value.equals("N") : value;
...
if (i == 1) {
...
}
else if (i == 2) {
...
} else {
assert false : "cannot happen. i is " + i;
}
...
这样,您一定会很早发现此错误。(在契约中指定此参数不应为null也很有用。)在开发过程中测试代码时,一定要打开断言。(说服你的同事这么做通常也很难,我觉得这很烦人。)
现在,您的一些同事会反对这段代码,认为您仍然应该进行null检查,以防止生产中出现异常。在这种情况下,断言仍然有用。你可以这样写:
void doSomething(Widget widget) {
assert widget != null;
if (widget != null) {
widget.someMethod(); // ...
... // do more stuff with this widget
}
}
这样,您的同事会很高兴产品代码有空检查,但在开发过程中,当小部件为空时,您不再隐藏错误
这里有一个真实的例子:我曾经写过一个方法
public class Assert {
static final boolean $assertionsDisabled =
!Assert.class.desiredAssertionStatus();
public static void main(String[] args) {
if (!$assertionsDisabled) {
if (System.currentTimeMillis() != 0L) {
throw new AssertionError();
}
}
}
}
javac Assert.java
javap -c -constants -private -verbose Assert.class
static final boolean $assertionsDisabled;
descriptor: Z
flags: ACC_STATIC, ACC_FINAL, ACC_SYNTHETIC
0: ldc #6 // class Assert
2: invokevirtual #7 // Method java/lang Class.desiredAssertionStatus:()Z
5: ifne 12
8: iconst_1
9: goto 13
12: iconst_0
13: putstatic #2 // Field $assertionsDisabled:Z
16: return
0: getstatic #2 // Field $assertionsDisabled:Z
3: ifne 22
6: invokestatic #3 // Method java/lang/System.currentTimeMillis:()J
9: lconst_0
10: lcmp
11: ifeq 22
14: new #4 // class java/lang/AssertionError
17: dup
18: invokespecial #5 // Method java/lang/AssertionError."<init>":()V
21: athrow
22: return
assert new HashSet<String>(Arrays.asList("Y", "N", null)).contains(value) : value;
assert value == null || value.equals("Y") || value.equals("N") : value;
assert boolean_expression;
assert boolean_expression: error_message;
...
if (i == 1) {
...
}
else if (i == 2) {
...
} else {
assert false : "cannot happen. i is " + i;
}
...
public boolean doSomething() {
...
}
public void someMethod() {
assert doSomething();
}
boolean enabled = false;
assert enabled = true;
if (enabled) {
System.out.println("Assertions are enabled");
} else {
System.out.println("Assertions are disabled");
}
assert Sets.newHashSet(userIds).size() == userIds.size();
public static int medianOf(int[] a, int[] b) {
assert assertionOnlyIsSorted(a); // Assertion is order n
assert assertionOnlyIsSorted(b);
... // rest of implementation goes here. Algorithm is order log(n)
}
public static boolean assertionOnlyIsSorted(int[] array) {
for (int i=1; i<array.length; ++i) {
if (array[i] < array[i-1]) {
return false;
}
return true;
}
}