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
|