assert 默认是关的。它在字节码里是什么样?-ea:p... 这样的选项怎么起作用?断言里有副作用会怎样?关闭时真的零成本吗?这篇逐项验证。
p.Lib 的字段: ['static final boolean $assertionsDisabled;']
p.Lib 的 <clinit> 字节码:
0: ldc #7 // class p/Lib
2: invokevirtual #9 // Method java/lang/Class.desiredAssertionStatus:()Z
5: ifne 12
8: iconst_1
9: goto 13
12: iconst_0
13: putstatic #21 // Field $assertionsDisabled:Z
16: return
run(int) 里 assert 的字节码:
0: getstatic #21 // Field $assertionsDisabled:Z
3: ifne 20
6: iload_0
7: ifgt 20
10: new #25 // class java/lang/AssertionError
14: ldc #27 // String p.Lib: x 必须为正
16: invokespecial #29 // Method java/lang/AssertionError."<init>":(Ljava/lang/Object;)V
19: athrow
20: ldc #32 // String 通过
0: ldc #7 // class p/Lib
5: ifne 12 [默认(不加选项)]
assert 没有触发(check 调用次数 0)
Main.class.desiredAssertionStatus() = false;p.Lib = false;q.Other = false
p.Lib.run(-5) → 通过;q.Other.run(-5) → 通过
[-ea]
assert 失败:java.lang.AssertionError: x 必须为正:-1(check 调用次数 1)
无消息的 assert:message=null,cause=null
assert 的详细信息是异常对象:message=java.lang.IllegalStateException: 对象作为详细信息,cause=IllegalStateException
Main.class.desiredAssertionStatus() = true;p.Lib = true;q.Other = true
p.Lib.run(-5) → AssertionError(p.Lib: x 必须为正);q.Other.run(-5) → AssertionError(q.Other: x 必须为正)
[-ea:p...(只打开包 p 及子包)]
assert 没有触发(check 调用次数 0)
Main.class.desiredAssertionStatus() = false;p.Lib = true;q.Other = false
p.Lib.run(-5) → AssertionError(p.Lib: x 必须为正);q.Other.run(-5) → 通过
[-ea:Main(只打开 Main 类)]
assert 失败:java.lang.AssertionError: x 必须为正:-1(check 调用次数 1)
无消息的 assert:message=null,cause=null
assert 的详细信息是异常对象:message=java.lang.IllegalStateException: 对象作为详细信息,cause=IllegalStateException
Main.class.desiredAssertionStatus() = true;p.Lib = false;q.Other = false
p.Lib.run(-5) → 通过;q.Other.run(-5) → 通过
[-ea -da:p...(全开再关掉 p)]
assert 失败:java.lang.AssertionError: x 必须为正:-1(check 调用次数 1)
无消息的 assert:message=null,cause=null
assert 的详细信息是异常对象:message=java.lang.IllegalStateException: 对象作为详细信息,cause=IllegalStateException
Main.class.desiredAssertionStatus() = true;p.Lib = false;q.Other = true
p.Lib.run(-5) → 通过;q.Other.run(-5) → AssertionError(q.Other: x 必须为正)
[-esa(只打开系统类)]
assert 没有触发(check 调用次数 0)
Main.class.desiredAssertionStatus() = false;p.Lib = false;q.Other = false
p.Lib.run(-5) → 通过;q.Other.run(-5) → 通过
[-ea:q.Other -da:Main]
assert 没有触发(check 调用次数 0)
Main.class.desiredAssertionStatus() = false;p.Lib = false;q.Other = true
p.Lib.run(-5) → 通过;q.Other.run(-5) → AssertionError(q.Other: x 必须为正) 默认选项下:
p.Lib 还没有被加载时:setClassAssertionStatus("p.Lib", true) 之前 desiredAssertionStatus 未知;设置之后加载它:
p.Lib.status() = true;p.Lib.run(-5) = AssertionError(p.Lib: x 必须为正)
p.Lib 已经初始化后再 setClassAssertionStatus("p.Lib", false):p.Lib.run(-5) = AssertionError(p.Lib: x 必须为正)(断言状态在初始化时已经写进 $assertionsDisabled,之后的修改不会改变它)
setDefaultAssertionStatus(true) 之后才加载的 q.Other:status() = true;run(-5) = AssertionError(q.Other: x 必须为正)不加 -ea(断言关闭) 每次迭代 0.58 ns(3 次进程的中位数 [0.58, 0.58, 0.57]) -ea(断言打开) 每次迭代 10.51 ns(3 次进程的中位数 [10.5, 10.54, 10.51])