Question

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;
    }
}
Était-ce utile?

La solution

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
    }

Autres conseils

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!

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

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.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top