|
|
|
Sponsored Link •
|
|
Advertisement
|
As mentioned in Chapter 7, "The Lifetime of a Class," references to static final variables initialized to a
compile-time constant are resolved at compile-time to a local copy of the constant value. This is true for
constants of all the primitive types and of type java.lang.String.
This special treatment of constants facilitates two features of the Java language. First, local copies of
constant values enable static final variables to be used as case expressions in
switch statements. The two virtual machine instructions that implement
switch statements in bytecodes, tableswitch and
lookupswitch, require the case values in-line in the bytecode
stream. These instructions do not support run-time resolution of case values. See
Chapter 16, "Control Flow," for more information about these instructions.
The other motivation behind the special treatment of constants is conditional compilation. Java supports
conditional compilation via if statements whose expressions resolve to a compile-time
constant. Here's an example:
// 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!");
}
}
}
Because of the special treatment of primitive constants, the Java compiler can decide whether or not to
include the body of the if statement in Example2.main()
depending upon the value of AntHill.debug. Because
AntHill.debug is true in this case,
javac generates bytecodes for Example2's
main() method that include the body of the if statement, but not
a check of AntHill.debug's value. The constant pool of
Example2 has no symbolic reference to class AntHill. Here are
the bytecodes for main():
// Push objref from System.out
0 getstatic #8
// Push objref to literal string "Debug is true!"
3 ldc #1
// Pop objref (to a String), pop objref(to
// System.out), invoke println() on System.out
// passing the string as the only parameter:
// System.out.println("Debug is true!");
5 invokevirtual #9
8 return // return void
If the reference to AntHill.debug were resolved at run-time, the compiler
would always need to include a check of AntHill.debug's value and the body of the
if statement just in case value of AntHill.debug ever changed.
The value of AntHill.debug can't change after it is compiled, of course, because it
is declared as final. Still, you could change the source code of AntHill and recompile
AntHill, but not recompile Example2.
Because the reference to AntHill.debug is resolved at compile-time the
compiler can conditionally compile out the body of the if statement if
AntHill.debug is discovered to be false. Note that this
means you can't change the behavior of the Example2 application just be setting
AntHill to false and recompiling only
AntHill. You have to recompile Example2 as well.
Example3, shown below, is Example2 with its name
changed to Example3 and compiled with an AntHill that has
debug set to false:
// On CD-ROM in file linking/ex3/AntHill.java
class AntHill {
static final boolean debug = false;
}
// On CD-ROM in file linking/ex3/Example3.java
class Example3 {
public static void main(String[] args) {
if (AntHill.debug) {
System.out.println("Debug is true!");
}
}
}
Here are the bytecodes generated by javac for Example3's
main() method:
0 return // return void
As you can see, the Java compiler has brazenly eliminated the entire if statement
found in Example3.main(). There is not even a hint of the
println() invocation in this very short bytecode sequence.
|
Sponsored Links
|