有哪些真实的例子来理解断言的关键作用?
当前回答
让我们假设您要编写一个控制核电站的程序。很明显,即使是最微小的错误也可能导致灾难性的结果,因此您的代码必须是无bug的(为了论证,假设JVM是无bug的)。
Java不是一种可验证的语言,这意味着:你不能计算出你的操作结果会是完美的。这样做的主要原因是指针:它们可以指向任何地方,也可以指向任何地方,因此它们不能被计算为这个确切的值,至少在合理的代码范围内不能。对于这个问题,没有办法证明您的代码在整体上是正确的。但你能做的是证明你至少能在bug发生时找到它。
此思想基于契约式设计(Design-by-Contract, DbC)范式:首先定义(具有数学精度)您的方法应该做什么,然后在实际执行期间通过测试来验证这一点。例子:
// Calculates the sum of a (int) + b (int) and returns the result (int).
int sum(int a, int b) {
return a + b;
}
虽然这很明显可以正常工作,但大多数程序员不会看到其中隐藏的bug(提示:Ariane V因为类似的bug而崩溃)。现在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,成本就越低)。此外,如果代码中有任何错误,您将检测到它。不可能出现漏洞并在以后引起问题。
这仍然不能保证代码没有错误,但它比通常的程序更接近于这一点。
其他回答
一个真实的例子,来自一个stack类(来自Java文章中的断言)
public int pop() {
// precondition
assert !isEmpty() : "Stack is empty";
return stack[--num];
}
Assert是一个关键字。它在JDK 1.4中引入。这是两种类型的断言
非常简单的断言语句 简单的断言语句。
默认情况下,所有assert语句都不会被执行。如果assert语句接收到false,则它将自动引发断言错误。
这是另一个例子。我写了一个方法来查找两个排序数组中值的中位数。该方法假设数组已经排序。出于性能考虑,它不应该首先对数组排序,甚至不应该检查以确保它们已排序。然而,对未排序的数据调用此方法是一个严重的错误,我们希望在开发阶段尽早发现这些错误。下面是我处理这些看似矛盾的目标的方法:
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;
}
}
这样,缓慢的测试只在开发阶段执行,在开发阶段,速度没有捕获错误重要。您希望medianOf()方法具有log(n)性能,但“is sorted”测试是o (n)。因此,我将其放在断言中,以限制其在开发阶段的使用,并为其命名,以明确表示它不适合生产。
这样我就两全其美了。在开发过程中,我知道任何不正确地调用这个函数的方法都会被捕获并修复。而且我知道这样做的缓慢测试不会影响生产中的性能。(这也很好地说明了为什么要在生产环境中关闭断言,而在开发环境中启用断言。)
总结一下(不仅仅是Java,很多语言都是如此):
“assert”主要被软件开发人员在调试过程中用作调试辅助。断言消息永远不会出现。许多语言提供了一个编译时选项,该选项将导致所有“断言”被忽略,用于生成“生产”代码。
"exceptions" are a handy way to handle all kinds of error conditions, whether or not they represent logic errors, because, if you run into an error-condition such that you cannot continue, you can simply "throw them up into the air," from wherever you are, expecting someone else out there to be ready to "catch" them. Control is transferred in one step, straight from the code that threw the exception, straight to the catcher's mitt. (And the catcher can see the complete backtrace of calls that had taken place.)
此外,该子例程的调用者不必检查子例程是否成功:“如果我们现在在这里,它一定成功了,因为否则它会抛出异常,我们现在就不会在这里!”这种简单的策略使得代码设计和调试变得非常非常容易。
异常方便地允许致命错误条件保持原样:“规则的异常”。而且,对于它们来说,由代码路径来处理,这也是“规则的例外……“高飞球!”
断言主要用于调试应用程序,或者用于替换某些应用程序的异常处理,以检查应用程序的有效性。
断言在运行时工作。这里有一个简单的例子,可以非常简单地解释整个概念——assert关键字在Java中做什么?(WikiAnswers)。
推荐文章
- 在流中使用Java 8 foreach循环移动到下一项
- 访问限制:'Application'类型不是API(必需库rt.jar的限制)
- 用Java计算两个日期之间的天数
- 如何配置slf4j-simple
- 在Jar文件中运行类
- 带参数的可运行?
- 我如何得到一个字符串的前n个字符而不检查大小或出界?
- 我可以在Java中设置enum起始值吗?
- Java中的回调函数
- c#和Java中的泛型有什么不同?和模板在c++ ?
- 在Java中,流相对于循环的优势是什么?
- Jersey在未找到InjectionManagerFactory时停止工作
- 在Java流是peek真的只是调试?
- Recyclerview不调用onCreateViewHolder
- 将JSON字符串转换为HashMap