有哪些真实的例子来理解断言的关键作用?


当前回答

在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关键字在Java中做什么?(WikiAnswers)。

断言(通过assert关键字)是在Java 1.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;
}

Assert在开发时非常有用。当代码正常工作时,某些事情就不能发生。它易于使用,并且可以永远保留在代码中,因为在现实生活中它将被关闭。

如果这种情况在现实生活中有任何可能发生,那么您必须处理它。

我喜欢它,但不知道如何在Eclipse/Android/ADT中打开它。即使在调试时,它也似乎是关闭的。(有一个关于这个的线程,但它指的是“Java虚拟机”,它不会出现在ADT运行配置中)。

在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()。

总结一下(不仅仅是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.)

此外,该子例程的调用者不必检查子例程是否成功:“如果我们现在在这里,它一定成功了,因为否则它会抛出异常,我们现在就不会在这里!”这种简单的策略使得代码设计和调试变得非常非常容易。

异常方便地允许致命错误条件保持原样:“规则的异常”。而且,对于它们来说,由代码路径来处理,这也是“规则的例外……“高飞球!”