Take the 2-minute tour ×
Stack Overflow is a question and answer site for professional and enthusiast programmers. It's 100% free, no registration required.

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;
    }
}
share|improve this question
    
Look at some of the related questions on the right. –  devnull Mar 16 at 4:57
6  
static initialization order matters here –  Kugathasan Abimaran Mar 16 at 5: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 at 5: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 at 5:09
1  
@devnull : Why this question is duplicate of that? –  Kugathasan Abimaran Mar 16 at 5:14
show 3 more comments

4 Answers

up vote 17 down vote accepted

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
    }
share|improve this answer
    
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 at 5:26
    
I do mention this case in the answer –  fge Mar 16 at 5: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 at 5:48
    
@SotiriosDelimanolis done. I am still not 100% satisfied with the answer though. –  fge Mar 16 at 5: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 at 5:53
show 4 more comments

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!

share|improve this answer
    
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 at 6:04
    
Re-phrased, @SotiriosDelimanolis. –  Richard JP Le Guen Mar 16 at 16:02
add comment

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

share|improve this answer
    
Why is the value of ans 5 in their code? –  Sotirios Delimanolis Mar 16 at 5:12
1  
@Sotirios : Answer updated –  Kugathasan Abimaran Mar 16 at 5:14
add comment

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.

share|improve this answer
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.