Java断言-$assertionsDisabled与$assertionsEnabled
java中的断言可编译为添加到测试中的专用合成静态布尔值-该建议在这里有很好的文档记录: 在其中,我们创造了 最终私有静态布尔值$assertionEnabled= ClassLoader.desiredAssertionStatus(类名称) 然后 变成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
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