Java断言-$assertionsDisabled与$assertionsEnabled

Java断言-$assertionsDisabled与$assertionsEnabled,java,assert,decompiler,Java,Assert,Decompiler,java中的断言可编译为添加到测试中的专用合成静态布尔值-该建议在这里有很好的文档记录: 在其中,我们创造了 最终私有静态布尔值$assertionEnabled= ClassLoader.desiredAssertionStatus(类名称) 然后 变成 static final /* synthetic */ boolean $assertionsDisabled; public void test1(String s) { if ((!(AssertTest.$assertio

java中的断言可编译为添加到测试中的专用合成静态布尔值-该建议在这里有很好的文档记录:

在其中,我们创造了

最终私有静态布尔值$assertionEnabled= ClassLoader.desiredAssertionStatus(类名称)

然后 变成

static final /* synthetic */ boolean $assertionsDisabled;

public void test1(String s) {
    if ((!(AssertTest.$assertionsDisabled)) && (s.equals("Fred"))) {
        throw new AssertionError();
    }
    System.out.println(s);
}

static {
    AssertTest.$assertionsDisabled = !(AssertTest.class.desiredAssertionStatus());
}
我找不到任何文档说明为什么他们进行了否定测试,而不是肯定测试——也就是说,最初的提案捕获了assertionsENABLED,现在我们使用assertionsDISABLED

我能想到的唯一一件事是,这可能(可能!)产生更好的分支预测,但对我来说,这似乎是一个相当蹩脚的猜测——Java哲学(几乎)总是让字节码变得简单,让JIT进行优化

(请注意,这不是关于断言如何工作的问题-我知道!)

(顺便说一句,很有趣的是,这导致了错误的教程!6.2.1,有人引用它来回应错误的测试!)


有什么想法吗?

布尔值实际上是用整数实现的。人们普遍认为,与零比较更快,但我不认为有任何理由使用disable而不是enabled


IMHO,因为false是布尔值的默认值,我尝试选择一个默认值为
false
的标志,在这种情况下,
$assertionEnabled
会更有意义。

尽管在查看反编译的java源代码时,看起来有多余的工作要做,但不能依赖于此,您需要查看字节码级别

请看一下eclipse编译器和oracle javac生成的字节码:

                    #0: getstatic Test.$assertionsDisabled
                    #3: ifne #23
                    (assertion code) #6: aload_1
                    (assertion code) #7: ldc "Fred"
                    (assertion code) #9: invokevirtual String.equals(Object)
                    (assertion code) #12: ifeq #23
                    (assertion code) #15: new AssertionError
                    (assertion code) #18: dup
                    (assertion code) #19: invokespecial AssertionError.<init>()
                    (assertion code) #22: athrow
                    #23: getstatic System.out (PrintStream)
                    #26: aload_1
                    #27: invokevirtual PrintStream.println(String)
                    #30: return
#0:getstatic测试。$assertionsDisabled
#3:ifne#23
(断言代码)#6:aload_1
(断言代码)#7:ldc“Fred”
(断言代码)#9:invokeVirtualString.equals(对象)
(断言代码)#12:ifeq#23
(断言代码)#15:新断言错误
(断言代码)#18:dup
(断言代码)#19:调用特殊断言错误。()
(断言代码)#22:athrow
#23:getstatic System.out(打印流)
#26:aload_1
#27:invokevirtual PrintStream.println(字符串)
#30:返回
请注意字节码#3-它不需要反转
测试。$assertionsDisabled
值,它只需要执行一次阴性测试(即,如果在字节码级别是阴性测试或阳性测试,则没有任何区别)


总之,它的实现是高效的,并且没有任何冗余工作被执行。

这实际上是有原因的,而不仅仅是为了更紧凑的字节码或更快的条件执行。如果查看,您将看到以下注释:

启用在其类或接口完成初始化之前执行的assert语句

还有一个包含初始化循环的示例:

类栏{
静止的{
Baz.testAsserts();
//将在Baz初始化之前执行!
}
}
类Baz扩展条{
静态无效测试集(){
布尔启用=假;
assert enabled=true;
System.out.println(“断言”+
(启用?“启用”:“禁用”);
}
}

由于
Bar
Baz
的超类,因此必须在
Baz
初始化之前对其进行初始化。但是,其初始值设定项在尚未初始化的
Baz
类的上下文中执行断言语句,因此没有机会设置
$assertionsDisabled
字段。在本例中,该字段有其默认值,所有操作都按照spec:assertion执行。如果我们有一个
$assertionEnabled
字段,则未初始化类的断言将不会执行,因此它将违反规范。

确实如此。这使得使用$assertionsDisabled更加令人困惑;)同样(相当明显),这个变量是常量,无论如何在JITted代码中都不存在。(通过-XX:+PrintAssembly验证)这不是原因<启用了
$assertions
的code>ifeq也可以正常工作。
public void test1(String s) {
    assert (!s.equals("Fred"));
    System.out.println(s);
}
static final /* synthetic */ boolean $assertionsDisabled;

public void test1(String s) {
    if ((!(AssertTest.$assertionsDisabled)) && (s.equals("Fred"))) {
        throw new AssertionError();
    }
    System.out.println(s);
}

static {
    AssertTest.$assertionsDisabled = !(AssertTest.class.desiredAssertionStatus());
}
                    #0: getstatic Test.$assertionsDisabled
                    #3: ifne #23
                    (assertion code) #6: aload_1
                    (assertion code) #7: ldc "Fred"
                    (assertion code) #9: invokevirtual String.equals(Object)
                    (assertion code) #12: ifeq #23
                    (assertion code) #15: new AssertionError
                    (assertion code) #18: dup
                    (assertion code) #19: invokespecial AssertionError.<init>()
                    (assertion code) #22: athrow
                    #23: getstatic System.out (PrintStream)
                    #26: aload_1
                    #27: invokevirtual PrintStream.println(String)
                    #30: return