当你声明一个变量
public final static boolean runInDebugMode = false;
compile-time constant美元.
constant variable是原语类型或String
类型的final
变量,用常量表达式(§15.29)初始化.
which means that
对常量变量(§4.12.4)字段的引用必须在编译时解析为常量变量的初始值设定项表示的值V
.
如果这样一个字段是static
,那么二进制文件中的代码中不应该出现对该字段的引用,包括声明该字段的类或接口.
换句话说,当你在任何地方写if(runInDebugMode)
,在编译时runInDebugMode
是false
,行为就像你写if(false)
一样,因为这个值必须在编译时解析,并且编译的类文件中没有对字段的引用.
您的用例已经在§14.22页中详细讨论过
然而,为了方便地将if
语句用于"条件编译"目的,实际规则有所不同.
例如,以下语句会导致编译时错误:
while (false) { x=3; }
因为无法访问语句x=3;
;但表面上相似的情况是:
if (false) { x=3; }
不会导致编译时错误.优化编译器可以意识到语句x=3;
永远不会被执行,并且可以 Select 从生成的class
文件中省略该语句的代码,但是在这里指定的技术意义上,语句x=3;
不被视为"不可访问".
这种不同处理的基本原理是允许程序员定义"标志"变量,例如:
static final boolean DEBUG = false;
然后编写如下代码:
if (DEBUG) { x=3; }
其 idea 是,应该可以将DEBUG
的值从false
更改为true
或从true
更改为false
,然后正确编译代码,而不对程序文本进行其他更改.
条件编译附带了一个警告.如果编译了一组使用"flag"变量的类——或者更准确地说,任何static
常量变量(§4.12.4)——并且省略了条件代码,那么以后只分发包含该标志定义的类或接口的新版本是不够的.
因此,这句话清楚地表明,这种形式的条件编译符合语言设计者的意图,编译器有权省略有问题的代码(所有相关的编译器都有).原则上,编译器不需要省略代码,但由于它会生成对编译代码中字段GVar.runInDebugMode
的引用,因此代码不能包含真正的条件.如果代码没有被省略,就必须以事实上无条件的方式跳过它.或者,通过goto
指令,或者,当以最天真的方式编译时,通过字面上的测试false
iconst_0; ifeq …
.这两种方法在解释执行模式下都是纳秒级的,对JIT编译器/优化器没有任何挑战.
值得一提的是,static final
个字段是受信任的字段,它们通常甚至不能通过反射进行更改.例如,由断言功能使用,就像在后台一样,包含assert
语句的类将在类初始化时初始化一个static final boolean
字段(因此它不是编译时常量),每个assert
语句将根据static final
变量的状态有条件地跳过其判断.早在Java 1.4时代,人们就得出结论,以这种方式依赖它,在JVM中消除必要的死代码是司空见惯的.
因此,即使将调试标志从编译时常量改为初始化时常量,对性能的影响也不会明显.但是现在使用它的方式是,代码已经在编译时被删除,并且无论如何都不依赖JVM.