有哪些真实的例子来理解断言的关键作用?
当前回答
除了这里提供的所有很棒的答案之外,官方Java SE 7编程指南还提供了关于使用assert的非常简洁的手册;通过几个准确的例子,说明了何时使用断言是一个好主意(重要的是,也是坏主意),以及它与抛出异常有何不同。
Link
其他回答
断言用于检查后置条件和“永不失败”的前提条件。正确的代码应该永远不会使断言失败;当它们触发时,它们应该指出一个错误(希望是在接近问题的实际位置的地方)。
断言的一个例子可能是检查一组特定的方法是否以正确的顺序被调用(例如,在迭代器中hasNext()在next()之前被调用)。
断言允许检测代码中的缺陷。您可以打开断言进行测试和调试,而在程序处于生产状态时关闭断言。
既然你知道它是真的,为什么还要坚持呢?只有当一切都正常工作时,这才是正确的。如果程序有一个缺陷,它实际上可能不是真的。在过程早期检测到这一点可以让您知道哪里出了问题。
assert语句包含此语句以及可选的String消息。
assert语句的语法有两种形式:
assert boolean_expression;
assert boolean_expression: error_message;
下面是一些基本规则,它们控制着断言应该在哪里使用,不应该在哪里使用。断言应该用于:
验证私有方法的输入参数。不是公共方法。当传递坏参数时,公共方法应该抛出常规异常。 在程序的任何地方来确保一个事实的有效性,而这个事实几乎肯定是正确的。
例如,如果你确定它只会是1或2,你可以使用这样的断言:
...
if (i == 1) {
...
}
else if (i == 2) {
...
} else {
assert false : "cannot happen. i is " + i;
}
...
在任何方法结束时验证后置条件。这意味着,在执行业务逻辑之后,您可以使用断言来确保变量或结果的内部状态与您所期望的一致。例如,打开套接字或文件的方法可以在末尾使用断言来确保确实打开了套接字或文件。
断言不应该用于:
验证公共方法的输入参数。由于断言可能并不总是被执行,因此应该使用常规异常机制。 对用户输入的内容验证约束。同上。 不应用于副作用。
例如,这不是一个正确的用法,因为断言在这里被用于调用doSomething()方法的副作用。
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");
}
基本上,“断言为真”会通过,“断言为假”会失败。让我们看看这是如何工作的:
public static void main(String[] args)
{
String s1 = "Hello";
assert checkInteger(s1);
}
private static boolean checkInteger(String s)
{
try {
Integer.parseInt(s);
return true;
}
catch(Exception e)
{
return false;
}
}
在Java中assert关键字是做什么的?
让我们看看编译后的字节码。
我们将得出结论:
public class Assert {
public static void main(String[] args) {
assert System.currentTimeMillis() == 0L;
}
}
生成几乎完全相同的字节码:
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();
}
}
}
}
其中Assert.class.desiredAssertionStatus()在命令行传递-ea时为true,否则为false。
我们使用System.currentTimeMillis()来确保它不会被优化掉(assert true;所做的那样)。
合成字段生成后,Java只需要在加载时调用Assert.class.desiredAssertionStatus()一次,然后将结果缓存到那里。参见:“静态合成”是什么意思?
我们可以用以下方法验证:
javac Assert.java
javap -c -constants -private -verbose Assert.class
在Oracle JDK 1.8.0_45中,生成了一个合成的静态字段(参见:“静态合成”是什么意思?):
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没有字节码级别的支持:它是Java语言的概念 assert可以用系统属性-Pcom.me很好地模拟。assert=true替换命令行上的-ea,并抛出新的AssertionError()。
Assert在开发时非常有用。当代码正常工作时,某些事情就不能发生。它易于使用,并且可以永远保留在代码中,因为在现实生活中它将被关闭。
如果这种情况在现实生活中有任何可能发生,那么您必须处理它。
我喜欢它,但不知道如何在Eclipse/Android/ADT中打开它。即使在调试时,它也似乎是关闭的。(有一个关于这个的线程,但它指的是“Java虚拟机”,它不会出现在ADT运行配置中)。
推荐文章
- 在流中使用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