This question already has an answer here:
Someone explain to me the differences between the following two statements?
A static final
variable initialized by a static
code block:
private static final String foo;
static { foo = "foo"; }
A static final
variable initialized by an assignment:
private static final String foo = "foo";
Static block give you more than simple statement. In this particular case is the same thing. Static section will be executed at class load time, before any instances constructed. You can call methods here and assign their results to static fields. And you can catch exceptions in static blocks.
The JLS describes a few special behaviors of what it calls constant variables, which are
final
variables (whetherstatic
or not) which are initialized with constant expressions ofString
or primitive type.Constant variables have a major difference with respect to binary compatibility: the values of constant variables become part of the class's API, as far as the compiler is concerned.
An example:
Here,
XFOO
is a "constant variable" andYFOO
is not, but they are otherwise equivalent. ClassZ
prints out each of them. Compile those classes, then disassemble them withjavap -v X Y Z
, and here is the output:Class X:
Class Y:
Class Z:
Things to notice in the disassembly, which tell you the differences between
X
andY
run deeper than syntactic sugar:XFOO
has aConstantValue
attribute, signifying that its value is a compile-time constant. WhereasYFOO
does not, and uses astatic
block with aputstatic
instruction to initialize the value at runtime.The
String
constant"xfoo"
has become part of classZ
's constant pool, but"yfoo"
has not.Z.main
uses theldc
(load constant) instruction to load"xfoo"
onto the stack directly from its own constant pool, but it uses agetstatic
instruction to load the value ofY.YFOO
.Other differences you will find:
If you change the value of
XFOO
and recompileX.java
but notZ.java
, you have a problem: classZ
is still using the old value. If you change the value ofYFOO
and recompileY.java
, classZ
uses the new value whether you recompileZ.java
or not.If you delete the
X.class
file entirely, classZ
still runs correctly.Z
has no runtime dependency onX
. Whereas if you delete theY.class
file, classZ
fails to initialize with aClassNotFoundException: Y
.If you generate documentation for the classes with javadoc, the "Constant Field Values" page will document the value of
XFOO
, but not the value ofYFOO
.The JLS describes the above effects constant variables have on compiled class files in §13.1.3:
And in §13.4.9:
The upshot is that if your public library exposes any constant variables, you must never change their values if your new library version is otherwise supposed to be compatible with code compiled against old versions of the library. It won't necessarily cause an error, but the existing code will probably malfunction since it will have outdated ideas about the values of constants. (If your new library version needs for classes which use it to be recompiled anyway, then changing constants doesn't cause this problem.)
Thus, initializing a constant with a block gives you more freedom to change its value, because it prevents the compiler embedding the value into other classes.
In this example, there's one subtle difference - in your first example,
foo
isn't determined to be a compile-time constant, so it can't be used as a case inswitch
blocks (and wouldn't be inlined into other code); in your second example it, is. So for example:That's valid when
foo
is deemed to be a constant expression, but not when it's "just" a static final variable.However, static initializer blocks are usually used when you have more complicated initialization code - such as populating a collection.
The timing for initialization is described in JLS 12.4.2; any static final fields which are considered as compile-time constants are initialized first (step 6) and initializers are run later (step 9); all initializers (whether they're field initializers or static initializers) are run in textual order.
An additional aspect: Consider the case when you have multiple static fields, and yes this is a corner case...
As stated in Jon Skeet's answer, the JLS defines the exact order of initialization. However, if for some reason you have to initialize multiple static attributes in a specific order, you may want to make the initialization sequence clearly visible in the code. When using direct field initialization: Some code formatters (and developers) may decide at some point to sort fields differently, this will directly impact how the fields get initialized and introduce unwanted effects.
By the way, if you want to follow common java coding conventions, you should use capital letters when defining 'constants' (final static fields).
--- edited reflecting Jon Skeet's comments ---
The only difference is the initialization time.
Java first initializes the members and then the static blocks.
The value of
foo
is initialized when the class is loaded and static initializers are run.Here, the value of
foo
will be a compile-time constant. So, in reality"foo"
will be available as part of th byte-code itself.