corejava11(7.4 使用断言)

7.4 使用断言

断言是防御性编程的常用习惯用法。在下面的部分中,您将学习如何有效地使用它们。

7.4.1 断言概念

假设您确信某个特定属性已经实现,并且您在代码中依赖该属性。例如,您可能正在计算

double y = Math.sqrt(x);

你确定x不是负的。可能是另一个计算的结果不能有负的结果,或者是一个方法的参数,它要求调用方只提供正的输入。尽管如此,您还是希望进行双重检查,而不是允许混淆的“非数字”浮点值进入您的计算中。当然,您可以抛出一个异常:

if (x < 0) throw new IllegalArgumentException("x < 0");

但即使在测试完成后,此代码仍保留在程序中。如果您有很多这种检查,程序可能会比它应该运行的慢很多。

断言机制允许您在测试期间放入检查,并在生产代码中自动删除它们。

Java语言有一个关键字assert。有两种形式:

assert condition;

以及

assert condition : expression;

这两个语句都评估条件,如果条件为false,则抛出AssertionError。在第二条语句中,表达式被传递给AssertionError对象的构造函数,并转换为消息字符串。

注意

表达式部分的唯一目的是生成消息字符串。AssertionError对象不存储实际的表达式值,因此以后不能查询它。正如JDK文档所述,这样做“会鼓励程序员尝试从断言失败中恢复过来,这会破坏工具的目的。”

要断言x是非负的,只需使用语句

assert x >= 0;

或者您可以将x的实际值传递给AssertionError对象,以便稍后显示它。

assert x >= 0 : x;

C++注意

C语言的assert宏将断言条件转换为字符串,在断言失败时打印该字符串。例如,如果assert(x>=0)失败,它将打印出“x>=0”是失败条件。在Java中,条件不是自动的错误报告的一部分。如果要查看它,必须将其作为字符串传递到AssertionError对象:assert x>=0 : "x>=0"

7.4.2 断言启用和禁用

默认情况下,断言被禁用。通过使用-enablesessions-ea选项运行程序来启用它们:

java -enableassertions MyApp

注意,您不必重新编译程序来启用或禁用断言。启用或禁用断言是类加载器的一个函数。当断言被禁用时,类加载器会去掉断言代码,这样不会减慢执行速度。

甚至可以在特定类或整个包中启用断言。例如:

java -ea:MyClass -ea:com.mycompany.mylib MyApp

此命令为类MyClasscom.mycompany.mylib包及其子包中的所有类打开断言。选项-ea…打开未命名包的所有类中的断言。

还可以使用-disableassertions-da选项禁用某些类和包中的断言:

java -ea:... -da:MyClass MyApp

有些类不是由类装入器装入的,而是直接由虚拟机装入的。您可以使用这些开关有选择地启用或禁用这些类中的断言。

但是,启用或禁用所有断言的-ea-da开关不适用于没有类加载器的“系统类”。使用-enablesystemassertions/-esa开关在系统类中启用断言。

也可以通过编程控制类加载器的断言状态。请参阅本节末尾的API注释。

7.4.3 使用断言进行参数检查

Java语言为您提供了处理系统故障的三种机制:

  • 扔出异常
  • 日志
  • 使用断言

什么时候应该选择断言?记住这些要点:

  • 断言失败是致命的、不可恢复的错误。
  • 断言检查仅在开发和测试期间打开。(这有时被戏称为“当你靠近海岸时穿着救生衣,当你在海洋中时把它扔到海里。”)

因此,您不会使用断言向程序的另一部分发送可恢复条件的信号,也不会使用断言向程序用户传递问题。断言只能用于在测试期间定位内部程序错误。

让我们来看一个常见的场景,方法参数的检查。您应该使用断言来检查是否存在非法的索引值或null引用吗?要回答这个问题,您必须查看方法的文档。假设您实现了一个排序方法。

/**
Sorts the specified range of the specified array in ascending num
The range to be sorted extends from fromIndex, inclusive, to toIn
@param a the array to be sorted.
@param fromIndex the index of the first element (inclusive) to be
@param toIndex the index of the last element (exclusive) to be so
@throws IllegalArgumentException if fromIndex > toIndex
@throws ArrayIndexOutOfBoundsException if fromIndex < 0 or toInde
*/
static void sort(int[] a, int fromIndex, int toIndex)

文档声明,如果索引值不正确,该方法将引发异常。该行为是方法与其调用方订立的契约的一部分。如果实现该方法,则必须尊重该契约并抛出指定的异常。相反,使用断言是不合适的。

是否应断言a不为null?这也不合适。当anull时,方法文档对方法的行为保持沉默。在这种情况下,调用方有权假定该方法将成功返回,并且不会引发断言错误。

@param a the array to be sorted (must not be null).

现在,方法的调用方已经注意到使用null数组调用方法是非法的。然后该方法可以从断言开始

assert a != null;

计算机科学家称这种契约为先决条件。最初的方法对其参数没有前提条件,它保证在所有情况下都有一个明确的行为。修改后的方法有一个前提:a不为null。如果调用方未能满足前提条件,那么所有的赌注都将被取消,该方法可以做任何它想做的事情。事实上,在断言就位后,当非法调用方法时,它有一个相当不可预测的行为。它有时抛出断言错误,有时抛出空指针异常,这取决于类加载器的配置方式。

7.4.4 使用断言记录假设

许多程序员使用注释来记录他们的基本假设。请考虑下面的示例:http://docs.oracle.com/javase/8/docs/technotes/guide

if (i % 3 == 0)
    . . .
else if (i % 3 == 1)
    . . .
else // (i % 3 == 2)
    . . .

在这种情况下,使用断言是很有意义的。

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

当然,深入思考这个问题会更有意义。i%3的可能值是多少?如果i是正数,则余数必须是0、1或2。如果i是负数,那么余数可以是-1或-2。因此,真正的假设是i不是负数的。一个更好的断言是

assert i >= 0;

if语句前面

无论如何,这个例子展示了断言作为程序员自我检查的良好使用。如您所见,断言是用于测试和调试的战术工具。相反,日志记录是程序整个生命周期的一个战略工具。我们将在下一节中检查日志记录。

java.lang.ClassLoader 1.0

  • void setDefaultAssertionStatus(boolean b) 1.4
    为没有显式类或包断言状态的类加载器加载的所有类启用或禁用断言。
  • void setClassAssertionStatus(String className, boolean b) 1.4
    启用或禁用给定类及其内部类的断言。
  • void setPackageAssertionStatus(String packageName, boolean b) 1.4
    为给定包及其子包中的所有类启用或禁用断言。
  • void clearAssertionStatus() 1.4
    删除所有显式类和包断言状态设置,并为此类加载器加载的所有类禁用断言。
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值