24

This problem was taken from my Java test study guide. Could some explain why this is the way that it is?

This code prints out the number 5 and not 12. Could you explain why? Could you explain why it would print out 12 if the second variable was also final, and 0 if neither of them were final?

public class Question26 {
    public static void main(String[] args) {
        System.out.println(Q26.q26.ans);
    }
}

class Q26 {
    public static Q26 q26 = new Q26();
    public int ans;
    private static final int var1 = 5;
    private static int var2 = 7;

    public Q26() {
        ans = var1 + var2;
    }
}
i.n.e.f
  • 1,773
  • 13
  • 22
user2837858
  • 339
  • 6
  • 18
  • Look at some of the `related` questions on the right. – devnull Mar 16 '14 at 04:57
  • 6
    static initialization order matters here – Abimaran Kugathasan Mar 16 '14 at 05:03
  • 5
    I _do_ hope that in your guide, this is quoted among things not to do? The only benefit of such a class is to shatter your brain to pieces for no benefit, since you will certainly not use such constructs in any real programming... – fge Mar 16 '14 at 05:05
  • 1
    It's for a test. Of course it's meant to shatter my brain into little pieces for no benefit. – user2837858 Mar 16 '14 at 05:09
  • I don't get it too. But I think it's related to declaring a new instance of the class (which is non-static) as a static field.. if you create new Q26 in the main method, the output is 12, regardless whether final is used for the two variables. – Kevin Lee Mar 16 '14 at 05:14
  • 1
    @devnull : Why this question is duplicate of that? – Abimaran Kugathasan Mar 16 '14 at 05:14
  • @devnull Not only is the question different than the one you linked in concept, but the attribute being discussed is different: this question is about `final` vs not `final`; the one you linked is about `static` vs not `static`. – Michael Yaworski Mar 16 '14 at 05:22

4 Answers4

20

One thing to know where you declare static fields is that those are initialized in order; you cannot write:

public class DoesNotCompile
{
    private static final int foo = 1 + bar; // ERROR: bar is not defined
    private static final int bar = 1;

In your situation, however, things are a little different:

class Q26 {
    // Declared first, but NOT first to be initialized...
    public static Q26 q26 = new Q26();
    public int ans;
    // The honor befalls to this one, since it is declared `final`
    private static final int var1 = 5;
    private static int var2 = 7; // zero until initialized

    public Q26() {
        ans = var1 + var2;
    }
}

The default value of a non initialized int is 0; since your Q26 instance is declared before var1 and var2, but since var1 is initialized first (since it is final), the result is what you see: ans is 5.

An equivalent to this code could be:

class Q26 {
    public static Q26 q26;
    private static final int var1;
    private static int var2;

    static {
        var1 = 5;
        q26 = new Q26();
        var2 = 7;
    }

    public int ans;

    public Q26() {
        ans = var1 + var2;
    }
}

Further note: there are also static initialization blocks; and order matters for these as well. You cannot do:

public class DoesNotCompileEither
{
    static {
        foo = 3; // ERROR: what is foo?
    }
    private static final int foo;

If you put the static initializer below the declaration of foo, then this will compile:

public class ThisOneCompiles
{
    private static final int foo; // declared
    static {
        foo = 3; // initialized
    }
fge
  • 119,121
  • 33
  • 254
  • 329
  • Do you have a citation to the effect that `static final` variables are computed at compile time? I don't believe that's the case - the initialization can be deferred to a `static` block: `class Question26{ private static final int var1; static { var1 = 5; } }`. Deferring to a static block, the final calculation can still be changed. – Richard JP Le Guen Mar 16 '14 at 05:26
  • I do mention this case in the answer – fge Mar 16 '14 at 05:28
  • You should complete your answer by explaining what would happen if the `static` block was after the declaration of the `static` variable. – Sotirios Delimanolis Mar 16 '14 at 05:48
  • @SotiriosDelimanolis done. I am still not 100% satisfied with the answer though. – fge Mar 16 '14 at 05:51
  • You're only missing the explanation of what happens then. What @Richard was getting at was what if the `Q26.var1` variable was initialized in a `static` block that came after the declaration of `q26`. What would happen and why, obviously. – Sotirios Delimanolis Mar 16 '14 at 05:53
  • Uuuh, wait; if you declare `static final int foo = 4; static { foo = 5 };`, that is a compile error (note: in this order, of course) – fge Mar 16 '14 at 05:57
  • But what if you left out the initialization expression at the declaration of `foo`? – Sotirios Delimanolis Mar 16 '14 at 05:58
  • Then it would compile, yes. But what is your point ultimately? – fge Mar 16 '14 at 06:11
  • See Richard's answer. My point is that in that case, `var1` would have a value of `0` when the constructor executes. You should highlight the order of execution on class initialization. – Sotirios Delimanolis Mar 16 '14 at 06:14
6

The static members on your class don't all magically initialize at the same time. Under the hood, Java has to set them - and it's doing it in the order you declared them, with exception of the final one.

Lets re-write that so the issue becomes clearer:

public class Question26 {
    public static void main(String[] args) {
        System.out.println(Q26.q26.ans);
    }
}
class Q26 {
    public static Q26 q26;
    public int ans;
    private static final int var1 = 5;
    private static int var2;

    static {
        q26 = new Q26();
        var2 = 7;
    }

    public Q26() {
        ans = var1 + var2;
    }
}

Here the declaration of non-final static members and their initialization have been separated. Putting the initialization in a static block makes it a little more evident that the initialization of those members is code like any other and has an order of execution.

But why is the final variable initialized before q26? Looking at this answer and at the Java specification, it seems that var1 might be getting treated as a compile-time constant expression. Even if it isn't being treated as such though, so long as it is declared and is initialized in one statement, var1 should be initialized by the runtime before the non-final variables:

At run time, static fields that are final and that are initialized with constant expressions are initialized first. This also applies to such fields in interfaces. These fields are "constants" that will never be observed to have their default initial values, even by devious programs.

Citation: http://docs.oracle.com/javase/specs/jls/se7/html/jls-8.html#jls-8.3.2.1

So what if we get crazy, separating the declaration and initialization of the static final member var1? In that case we could actually make your program compile and execute, only to output 0 - which contradicts some of the assertions you made in your question.

public class Question26 {
    public static void main(String[] args) {
        System.out.println(Q26.q26.ans);
    }
}
class Q26 {
    public static Q26 q26;
    public int ans;
    private static final int var1;
    private static int var2;

    static {
        q26 = new Q26();
        var1 = 5;
        var2 = 7;
    }

    public Q26() {
        ans = var1 + var2;
    }
}

So don't be fooled into thinking the keywords you use to declare variables ensures some specific execution order!

Community
  • 1
  • 1
Richard JP Le Guen
  • 28,364
  • 7
  • 89
  • 119
  • Ah, Richard. I don't like your wording. `But why is the final variable initialized out of order?` It's not. From the JLS, `Next, execute either the class variable initializers and static initializers of the class, or the field initializers of the interface, in textual order, as though they were a single block.` – Sotirios Delimanolis Mar 16 '14 at 06:04
3

That is because of the order of initialization of static members, they will be initialized the textual order which they declared. Define the int variables var1 and var2 before that object variable q26 like below

class Q26 {
    private static final int var1 = 5;
    private static int var2 = 7;
    public static Q26 q26 = new Q26();
    public int ans;


    public Q26() {
        ans = var1 + var2;
    }
}

Now Output is 12

In your case, the object q26 was initialized before the variable var2, but var1 is a final vaiable, the value is know at compile time. So you got that answer

Abimaran Kugathasan
  • 31,165
  • 11
  • 75
  • 105
1

From http://docs.oracle.com/javase/tutorial/java/javaOO/classvars.html

If a primitive type or a string is defined as a constant and the value is known at compile time, the compiler replaces the constant name everywhere in the code with its value. This is called a compile-time constant. If the value of the constant in the outside world changes (for example, if it is legislated that pi actually should be 3.975), you will need to recompile any classes that use this constant to get the current value.

Therefore despite the fact of having that variable defined after the static q26, the compiler had replaced each place var1 appeared as 5.

Abimaran Kugathasan
  • 31,165
  • 11
  • 75
  • 105
mgo1977
  • 81
  • 3