84

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";
Roman C
  • 49,761
  • 33
  • 66
  • 176
Fabio_M
  • 879
  • 7
  • 21

7 Answers7

101

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 in switch blocks (and wouldn't be inlined into other code); in your second example it, is. So for example:

switch (args[0]) {
    case foo:
        System.out.println("Yes");
        break;
}

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.

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • You can't use objects in switch case statements! Enum and primitive types are the only allowed. – Fabio_M Apr 17 '15 at 06:13
  • 7
    @FabioMarano in java 7 it's possible – Madhawa Priyashantha Apr 17 '15 at 06:14
  • 4
    @FabioMarano: You can use string constants. As Fast Snail says, this was introduced in Java 7... – Jon Skeet Apr 17 '15 at 06:15
  • 4
    @AnkitLamba - You should google *Jon Skeet facts* on google and click the first link that will redirect you to meta. Sorry *Jon*, they have to know your secret :P – TheLostMind Apr 17 '15 at 06:18
  • 1
    @TheLostMind yeah, I have read those a long time ago... *he is really the guru* – earthmover Apr 17 '15 at 06:20
  • @JonSkeet - I don't get the *compile-time constants are initialized first* part. Arent *compile-time constants* supposed to be present in the bytecode itself? – TheLostMind Apr 17 '15 at 06:24
  • 4
    @TheLostMind: Yes, but they're *also* present in the fields. So if you have a static initializer which occurs *before* a field which is initialized with a compile-time constant, and you use reflection to access the field, you'll still see the constant value in the field. (I've just checked.) – Jon Skeet Apr 17 '15 at 06:29
  • Any difference wrt *effective final* in this scenario ? (If we don't declare field as `final` explicitly) – Not a bug Apr 17 '15 at 06:47
  • @KisHanSarsecHaGajjar: "Effective final" is all about *local* variables rather than fields, as far as I'm aware. – Jon Skeet Apr 17 '15 at 07:21
  • @JonSkeet oups Jon, I was not, thanks !! – Not a bug Apr 17 '15 at 07:38
  • 3
    To be a bit more precise, compile-time constants held in `static final` variables are initialized by the JVM before the static initializer code is run, whereas non-`static` `final` fields might also hold compile-time constants but are initialized by the constructor like ordinary instance fields. However, you will need Reflection to notice the difference as any ordinary reference to such a field will be replaced by the constant value at compile-time… – Holger Apr 17 '15 at 08:23
  • @Holger: I've reworded a bit; I view it as "If the compiler considers the field to be a constant expression elsewhere, it's initialized before everything else." Does that work for you? – Jon Skeet Apr 17 '15 at 08:37
  • That works. After all, it’s not supposed to be an iron-hard specification (as that’s what the specification itself is for). If I was nitpicking I said that “timing” isn’t the right word as it’s about *ordering* of actions but there are no timing guarantees (but I’m not ;^) – Holger Apr 17 '15 at 08:47
  • @FabioMarano java 7+ uses hashcode() and equals() to switch the strings – alex Apr 17 '15 at 18:27
  • private static final int FOO; static { FOO =5;} public static void main(String x[]){ switch (FOO) { case 5: System.out.println("Assigned value by static block is available to FOO in switch case."); break; default: System.out.println("not available"); break; } String[] stringArray=new String[FOO]; } in my code value of Foo is available. –  Apr 22 '15 at 05:52
  • @dubey-theHarcourtians: Yes, you're switching with `FOO` as the *value*, not as one of the *cases*. Try using `switch (x.length) { case FOO: ... }` and you'll see *that* won't compile. – Jon Skeet Apr 22 '15 at 05:56
  • yes exactly jon.why FOO is not working in case: while it is working is Switch().One more thing how value assigned by static block to variable is available as a size of array at compile time? –  Apr 22 '15 at 06:05
  • 1
    @dubey-theHarcourtians: It's not working as a `case` because it's not a compile-time constant expression. And the array part is simple: array sizes don't have to be constants in Java. The size *isn't* evaluated at compile-time - it's evaluated at execution time. Both of the uses of `FOO` in your code are entirely legal for any `int` expression - you could change it to use a randomly-generated number and it would still be valid. – Jon Skeet Apr 22 '15 at 06:11
  • means case:'s are evaluated at compile time while switch's are at the time of execution? –  Apr 22 '15 at 06:18
  • @dubey-theHarcourtians: Yes. That's just basic Java - it feels like we're moving off the topic of the question, to be honest. I suggest if you have further questions about how switch/case works, you ask in a new post - after reading an appropriate book or tutorial, of course. – Jon Skeet Apr 22 '15 at 06:19
34
 private static final String foo;
 static { foo ="foo";}

The value of foo is initialized when the class is loaded and static initializers are run.

private static final String foo = "foo";

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.

TheLostMind
  • 35,966
  • 12
  • 68
  • 104
  • as you told static variable will be available as part of the byte-code itself.I want to make confirm in case of static method byte-code knows about the behavior of this static method or this will be resolved when method is called?@TheLostMind –  Apr 17 '15 at 10:38
  • 1
    @dubey-theHarcourtians - Methods behave in a *different way* than fields. All method calls (*static or otherwise*) will be resolved during *compile time*. The actual call will be made during *run-time*. The *byte-code* still has info about which method should be called and with what arguments – TheLostMind Apr 17 '15 at 10:43
  • @dubey-theHarcourtians - Yes. Methods that bind during compile time cannot be *overidden*. The [bytecode instruction used](http://en.wikipedia.org/wiki/Java_bytecode_instruction_listings) will be `invokestatic` . All method references are actually stored in the constant pool / method area of heap (Static or otherwise). For instance level calls it will be `invokevirtual`. – TheLostMind Apr 17 '15 at 10:50
  • 1
    @dubey-theHarcourtians - Objects don't exist at *instance level* or *class level*, they all go into the heap. Its the references that are *static / non-static*. In case of *composition*, the references to *composed objects* go into the heap as part of the *parent object*. In case of methods, references reside on the *stack frame* of the method . Local primitives always reside on the stack. All methods irrespective of static or otherwise go into the method area. – TheLostMind Apr 17 '15 at 11:14
  • thanks @TheLostMind.one more thing, even I know object reside in heap and almost entire code goes in method area and these method executed in separate stack.I want to make confirm where does i)-instance variable ii)-static variable iii)-local variable iv)-static method code reside in memory by JVM.please make me confirm i am confused because of various author have have various details.@Jon Skeet –  Apr 17 '15 at 11:17
10

In IInd case- value of foo is early bind ie compiler identifies and assign value foo to variable FOO, which cant be changed,and this will be available apart with byte-code itself.

private static final String FOO = "foo";

and In Ist case-value of foo initialize just after class loading as a very first assignment before instance variable assigned,also here you can catch exceptions or static field can be assign by calling static methods in static block.

private static final String FOO;
static { FOO ="foo";}

So whenever there is a condition arrive when compiler must have to identify the value of variable foo, condition II will work,for ex-like value of case: in switch cases.

8

The JLS describes a few special behaviors of what it calls constant variables, which are final variables (whether static or not) which are initialized with constant expressions of String 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:

class X {
    public static final String XFOO = "xfoo";
}

class Y {
    public static final String YFOO;
    static { YFOO = "yfoo"; }
}

class Z {
    public static void main(String[] args) {
        System.out.println(X.XFOO);
        System.out.println(Y.YFOO);
    }
}

Here, XFOO is a "constant variable" and YFOO is not, but they are otherwise equivalent. Class Z prints out each of them. Compile those classes, then disassemble them with javap -v X Y Z, and here is the output:

Class X:

Constant pool:
   #1 = Methodref          #3.#11         //  java/lang/Object."<init>":()V
   #2 = Class              #12            //  X
   #3 = Class              #13            //  java/lang/Object
   #4 = Utf8               XFOO
   #5 = Utf8               Ljava/lang/String;
   #6 = Utf8               ConstantValue
   #7 = String             #14            //  xfoo
   #8 = Utf8               <init>
   #9 = Utf8               ()V
  #10 = Utf8               Code
  #11 = NameAndType        #8:#9          //  "<init>":()V
  #12 = Utf8               X
  #13 = Utf8               java/lang/Object
  #14 = Utf8               xfoo
{
  public static final java.lang.String XFOO;
    descriptor: Ljava/lang/String;
    flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL
    ConstantValue: String xfoo


  X();
    descriptor: ()V
    flags:
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: return
}

Class Y:

Constant pool:
   #1 = Methodref          #5.#12         //  java/lang/Object."<init>":()V
   #2 = String             #13            //  yfoo
   #3 = Fieldref           #4.#14         //  Y.YFOO:Ljava/lang/String;
   #4 = Class              #15            //  Y
   #5 = Class              #16            //  java/lang/Object
   #6 = Utf8               YFOO
   #7 = Utf8               Ljava/lang/String;
   #8 = Utf8               <init>
   #9 = Utf8               ()V
  #10 = Utf8               Code
  #11 = Utf8               <clinit>
  #12 = NameAndType        #8:#9          //  "<init>":()V
  #13 = Utf8               yfoo
  #14 = NameAndType        #6:#7          //  YFOO:Ljava/lang/String;
  #15 = Utf8               Y
  #16 = Utf8               java/lang/Object
{
  public static final java.lang.String YFOO;
    descriptor: Ljava/lang/String;
    flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL


  Y();
    descriptor: ()V
    flags:
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: return

  static {};
    descriptor: ()V
    flags: ACC_STATIC
    Code:
      stack=1, locals=0, args_size=0
         0: ldc           #2                  // String yfoo
         2: putstatic     #3                  // Field YFOO:Ljava/lang/String;
         5: return
}

Class Z:

Constant pool:
   #1 = Methodref          #8.#14         //  java/lang/Object."<init>":()V
   #2 = Fieldref           #15.#16        //  java/lang/System.out:Ljava/io/PrintStream;
   #3 = Class              #17            //  X
   #4 = String             #18            //  xfoo
   #5 = Methodref          #19.#20        //  java/io/PrintStream.println:(Ljava/lang/String;)V
   #6 = Fieldref           #21.#22        //  Y.YFOO:Ljava/lang/String;
   #7 = Class              #23            //  Z
   #8 = Class              #24            //  java/lang/Object
   #9 = Utf8               <init>
  #10 = Utf8               ()V
  #11 = Utf8               Code
  #12 = Utf8               main
  #13 = Utf8               ([Ljava/lang/String;)V
  #14 = NameAndType        #9:#10         //  "<init>":()V
  #15 = Class              #25            //  java/lang/System
  #16 = NameAndType        #26:#27        //  out:Ljava/io/PrintStream;
  #17 = Utf8               X
  #18 = Utf8               xfoo
  #19 = Class              #28            //  java/io/PrintStream
  #20 = NameAndType        #29:#30        //  println:(Ljava/lang/String;)V
  #21 = Class              #31            //  Y
  #22 = NameAndType        #32:#33        //  YFOO:Ljava/lang/String;
  #23 = Utf8               Z
  #24 = Utf8               java/lang/Object
  #25 = Utf8               java/lang/System
  #26 = Utf8               out
  #27 = Utf8               Ljava/io/PrintStream;
  #28 = Utf8               java/io/PrintStream
  #29 = Utf8               println
  #30 = Utf8               (Ljava/lang/String;)V
  #31 = Utf8               Y
  #32 = Utf8               YFOO
  #33 = Utf8               Ljava/lang/String;
{
  Z();
    descriptor: ()V
    flags:
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: return

  public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=2, locals=1, args_size=1
         0: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
         3: ldc           #4                  // String xfoo
         5: invokevirtual #5                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
         8: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
        11: getstatic     #6                  // Field Y.YFOO:Ljava/lang/String;
        14: invokevirtual #5                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
        17: return
}

Things to notice in the disassembly, which tell you the differences between X and Y run deeper than syntactic sugar:

  • XFOO has a ConstantValue attribute, signifying that its value is a compile-time constant. Whereas YFOO does not, and uses a static block with a putstatic instruction to initialize the value at runtime.

  • The String constant "xfoo" has become part of class Z's constant pool, but "yfoo" has not.

  • Z.main uses the ldc (load constant) instruction to load "xfoo" onto the stack directly from its own constant pool, but it uses a getstatic instruction to load the value of Y.YFOO.

Other differences you will find:

  • If you change the value of XFOO and recompile X.java but not Z.java, you have a problem: class Z is still using the old value. If you change the value of YFOO and recompile Y.java, class Z uses the new value whether you recompile Z.java or not.

  • If you delete the X.class file entirely, class Z still runs correctly. Z has no runtime dependency on X. Whereas if you delete the Y.class file, class Z fails to initialize with a ClassNotFoundException: 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 of YFOO.

The JLS describes the above effects constant variables have on compiled class files in §13.1.3:

A reference to a field that is a constant variable (§4.12.4) must be resolved at compile time to the value V denoted by the constant variable's initializer.

If such a field is static, then no reference to the field should be present in the code in a binary file, including the class or interface which declared the field. Such a field must always appear to have been initialized (§12.4.2); the default initial value for the field (if different than V) must never be observed.

If such a field is non-static, then no reference to the field should be present in the code in a binary file, except in the class containing the field. (It will be a class rather than an interface, since an interface has only static fields.) The class should have code to set the field's value to V during instance creation (§12.5).

And in §13.4.9:

If a field is a constant variable (§4.12.4), and moreover is static, then deleting the keyword final or changing its value will not break compatibility with pre-existing binaries by causing them not to run, but they will not see any new value for a usage of the field unless they are recompiled.

[...]

The best way to avoid problems with "inconstant constants" in widely-distributed code is to use static constant variables only for values which truly are unlikely ever to change. Other than for true mathematical constants, we recommend that source code make very sparing use of static constant variables.

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.

Boann
  • 48,794
  • 16
  • 117
  • 146
2

The only difference is the initialization time.

Java first initializes the members and then the static blocks.

David Limkys
  • 4,907
  • 5
  • 26
  • 38
  • 2
    I thought that was the only difference too - but it's not. See my answer. – Jon Skeet Apr 17 '15 at 06:09
  • 3
    And also, initializers - both field *and* static initializers - are run in textual order. (I've just checked.) So if you have a static field with an initializer, then a static block, then another static field with an initializer, the static block will be run *before* the final initializer. Only constants values are initialized first. See http://docs.oracle.com/javase/specs/jls/se8/html/jls-12.html#jls-12.4.2 – Jon Skeet Apr 17 '15 at 06:12
2

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 ---

Danilo Tommasina
  • 1,740
  • 11
  • 25
  • 2
    "the JVM decides on the order" - no it doesn't. Field and static initializers are executed in textual order. See JLS 12.4.2. – Jon Skeet Apr 17 '15 at 06:15
  • It s a my tablet fault :-)@danilo – Fabio_M Apr 17 '15 at 06:28
  • @Jon Skeet Maybe 'decides' is the wrong word choice, a 'determines' is a better choice, yes absolutely correct the JLS specifies the exact order of initialization, still you have to assume that one knows all the wordings of the JLS and that e.g. no code-formatter or developer decides to sort the fields differently than what you did. I'll reformulate my answer to better reflect what I wanted to say. Thanks for the comment – Danilo Tommasina Apr 17 '15 at 07:36
  • 1
    But the same rules apply whether the developer knows them or not. Even without that aspect, it would be javac that determines it, not the JVM... looking at the bytecode, it's just a single static initializer block. – Jon Skeet Apr 17 '15 at 07:39
  • Correct, removed also the reference to the JVM ;) – Danilo Tommasina Apr 17 '15 at 07:46
-2

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.

szarlih
  • 135
  • 9
  • 1
    That doesn't really talk about the difference between static initializer blocks and static field initializers though, which is what the question was asking. – Jon Skeet Apr 17 '15 at 07:22