8.1.11编译时常量解析

8.1.11编译时常量解析

在第7章讲过,被初始化为编译时常量的静态final变量的引用,在编译时被解析为常量值的 一个本地拷贝,这对于所有的基本类型和java.lang.String都是正确的。

这种对于常量的特别处理使Java语言具有了两个特性。首先,常量值的本地拷贝使得静态 final变量可以用于switch语句中的case表达式。在字节码中实现switch语句的两条虚拟机指令是 tableswitch和lookupswitch,需要case值内嵌在字节码流中。这些指令不支持运行时解析case值。 要了解这两条指令的更多信息,请参阅第16章。

隐藏在常量的特殊处理后面的另一个动机是条件编译。通过if语句(其表达式解析成编译时常量),java支持条件编译。下面是一个例子:

// On CD-ROM in file linking/ex2/AntHill.java
class AntHill {

static final boolean debug = true;
}


// On CD-ROM in file linking/ex2/Example2.java
class Example2 {

public static void main(String[] args) {
if (AntHill.debug) {
System.out.println("Debug is true!");
}
}
}

因为基本类型常量的特别处理,Java编译器可以通过AntHill.debug的值,决定是否包括 Example2.main ()中的if语句体。因为在这个情形下AntHill.debug是true, javac为Example2的main()方法生成的字节码中就包含if语句的语句体,但是并不包括检查AntHill.debug的值。在 Example2的常量池中并没有指向类AntHill的符号引用。下面是main ()方法的字节码:

如果指向AntHill.debug的引用是在运行时解析的,编译器就需要检查AntHill.debug的值和if语句的语句体,以防AntHill.debug的值改变了。实际上AntHill.debug的值编译之后就不可能改变, 因为它声明为final。然而,仍然可以改变AntHill的源代码并重新编译AntHill,但是不重新编译 Examp1e2。

因为指向AntHill.debug的引用是在编译时解析的,编译器如果发现AntHill.debug是false,编译器就会有条件地编译if语句的语句体。请注意,这意味着如果只是把AntHill设置为false,只重新编译AntHill,而无法改变Example2程序的行为。必须也重新编译Example2。

下面的Example3,是把Example2的名字换成了Example3,并且把AntHill的debug值设置为 false时的编译结果。

下面是javac生成的Example3的main ()方法的字节码。

0 return // return void

可以看到,Java编译器把整个if语句都从Example3.main()方法中去处了。在这个短短的 字节码序列中,甚至没有任何println ()调用的提示。

猜你喜欢

转载自www.cnblogs.com/mongotea/p/11979618.html