7

I have class AbstractsAndInterfaces:

public static AbstractsAndInterfaces instance = new AbstractsAndInterfaces();
private static final int DELTA = 5;

private static int BASE = 7;

private int x;
public AbstractsAndInterfaces()
{
    //System.out.println(BASE);
    //System.out.println(DELTA);
    x = BASE + DELTA;
}
public static int getBASE()
{
    return BASE;
}

   /**
 * @param args the command line arguments
 */
public static void main(String[] args) {

    System.out.println(AbstractsAndInterfaces.instance.x);
}

Why does it print 5?

Why BASE variable is uninitialized?

Maroun
  • 94,125
  • 30
  • 188
  • 241
storojs72
  • 185
  • 1
  • 8
  • 2
    Essentially: http://stackoverflow.com/questions/27859435/java-static-final-field-initialization-order – marvin82 Apr 07 '15 at 11:33

1 Answers1

10

That's because :

private static final int DELTA = 5; will be a compile-time constant. So, 5 will already be available (initialized) when the class gets initialized (once it is loaded).

The first line that gets executed is : public static AbstractsAndInterfaces instance = new AbstractsAndInterfaces();

So, now you will go to :

public AbstractsAndInterfaces()
{
    //System.out.println(BASE);
    //System.out.println(DELTA);
    x = BASE + DELTA; // same as x= 0+5 i.e default value of fields + constant value 5
}

In the above code, DELTA will be 5 but BASE will be 0 as it is not final. Changing BASE to final will change the result to 12.

EDIT :

Sample code to show the OP's scenario with byte code.

public class Sample {
    static Sample s = new Sample();
    static final int x = 5;
    static int y = 10;

    public Sample() {
        int z = x + y;
        System.out.println(z);
    }

    public static void main(String[] args) {

    }

}

In the above code, when the program is run, the output will be 5 and not 10. Now let us look at the byte-code.

The byte code of constructor looks like this :

p

ublic Sample();
   descriptor: ()V
   flags: ACC_PUBLIC
   Code:
     stack=2, locals=2, args_size=1
        0: aload_0
        1: invokespecial #24                 // Method java/lang/Object."<init>
:()V 
        4: iconst_5       ------->           // Here 5 is used directly as it is a compile time constant
        5: getstatic     #20   ------->      // Field y:I
        8: iadd
        9: istore_1
       10: getstatic     #25                 // Field java/lang/System.out:Ljav
/io/PrintStream;
       13: iload_1
       14: invokevirtual #31                 // Method java/io/PrintStream.prin
ln:(I)V
       17: return

Byte code for static init block :

static {}; descriptor: ()V flags: ACC_STATIC Code: stack=2, locals=0, args_size=0 0: new #1 // class Sample 3: dup 4: invokespecial #15 // Method "":()V 7: putstatic #18 // Field s:LSample; 10: bipush 10 12: putstatic #20 // Field y:I 15: return LineNumberTable: line 3: 0 line 5: 10 line 1: 15 LocalVariableTable: Start Length Slot Name Signature

If you check carefully, x is not being initialized in the static init of class. Only y is being set as it is static. But when you look at the constructor, you will see that the constant 5 is directly being used (iconst_5) and pushed onto the stack.

Now if you add the following code in main() :

public static void main(String[] args) {
    System.out.println(Sample.x);
    System.out.println(Sample.y);
}

The byte code for main() will be :

   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     #25                 // Field java/lang/System.out:Ljav
a/io/PrintStream;
         3: iconst_5 -->                      // No "x" but a constant value instead of "x"
         4: invokevirtual #31                 // Method java/io/PrintStream.prin
tln:(I)V
         7: getstatic     #25                 // Field java/lang/System.out:Ljav
a/io/PrintStream;
        10: getstatic     #20                 // Field y:I --> but "y" is being fetched
        13: invokevirtual #31                 // Method java/io/PrintStream.prin
tln:(I)V
        16: return
      LineNumberTable:
        line 13: 0
        line 14: 7
        line 15: 16
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0      17     0  args   [Ljava/lang/String;
}

Again observe that x is being used as iconst_5whereas y is being fetched/referred indirectly.

TheLostMind
  • 35,966
  • 12
  • 68
  • 104
  • your last line is wrong change it!! – Prashant Apr 07 '15 at 11:23
  • 2
    @Prashant - Ya.. Typo, got confused with variable names.. Changed it now :) – TheLostMind Apr 07 '15 at 11:24
  • 2
    Shouldn't all fields be initialized before the constructor is called? That's what the [JLS](http://docs.oracle.com/javase/specs/jls/se8/html/jls-12.html#jls-12.4) states.. I think it worth mentioning why `BASE` is still uninitialized. – Maroun Apr 07 '15 at 11:38
  • So, static (without final) variable will be initialised only at runtime, correct? – storojs72 Apr 07 '15 at 11:39
  • 1
    This is really not the reason at all, and making it `final` is only one solution. Another would be to declare `BASE` before `instance.` – user207421 Apr 07 '15 at 12:01
  • @MarounMaroun - Not necessarily. In the OP's case, the *constructor* will be called before `Base` is *initialized*. The constructor call is *part of* `instance ` *initialization*. I have edited my answer. `Base` is `0` because the *class initialization* is still *in progress* and its value is used before it is actually initialized. Since `Delta` is a *compile-time* constant, its value will be available . Check byte code :) – TheLostMind Apr 07 '15 at 13:38
  • @EJP - Check my edited answer. It is the reason. The value 5 will be turned into a *compile-time-constant*. Yes, I agree, putting the initialization of `instance` after `Delta` and `base` will indeed give the OP the correct value i.e, `delta`+`base` . But if you look at the byte-code, the static init block doesn't initialize `static` `final` value. The value *is always available* once the class is loaded. Check my edited answer. *This is really not the reason at all* --> This is the reason. If there is another reason, I would be glad to hear and understand it :). – TheLostMind Apr 07 '15 at 13:45
  • @MarounMaroun - Edited my answer again and added additional byte code :) – TheLostMind Apr 07 '15 at 13:53
  • @TheLostMind how did you get the byte code from class file.I tried for the same using ***javap -c Sample.class***, but got different output.Thanks. – rns Sep 08 '15 at 10:01
  • 1
    @rns - Try `javap -v Sample.class` :) – TheLostMind Sep 08 '15 at 10:02