Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does a static variable initialized by a method call that returns another static variable remains null? [duplicate]

I simply don't understand the following code's execution flow:

class Test {
    static String s1 = getVal();
    static String s2 = "S2";

    private static String getVal() {
        return s2;
    }

    public static void main(String args[]) {
        System.out.println(s2); // prints S2
        System.out.println(s1); // prints null
    }
}

It is supposed to print S2 at second println statement. I am more interested in understanding why this happens rather than a solution.

like image 353
Aniket Sahrawat Avatar asked Jan 30 '18 06:01

Aniket Sahrawat


People also ask

Why static variables are initialized only once?

When static keyword is used, variable or data members or functions can not be modified again. It is allocated for the lifetime of program. Static functions can be called directly by using class name. Static variables are initialized only once.

Can we initialize static variable with another variable?

You can define a static field using the static keyword. If you declare a static variable in a class, if you haven't initialized it, just like with instance variables compiler initializes these with default values in the default constructor. Yes, you can also initialize these values using the constructor.

Can static variable be initialized twice?

But in some cases it generates code that uses two guard variables and initialize each static variable separately. The problem is when such two types of initialization are mixed in executable binary. In such case it may happen that second static variable will get initialized twice.

How are static variables initialized explain with the statement?

The only way to initialize static final variables other than the declaration statement is Static block. A static block is a block of code with a static keyword. In general, these are used to initialize the static members. JVM executes static blocks before the main method at the time of class loading.


2 Answers

Static things gets executed in the order they appear in the code.

static String s1 = getVal();

So starting with first line, s1 gets evaluated and by that time s2 is still null. Hence you see the null value.

like image 144
Suresh Atta Avatar answered Oct 16 '22 07:10

Suresh Atta


static variables and static initializer blocks are initialized in the order they appear in the source code (except of static final variables that get initialized before non-final static variables).

s1 is initialized before s2, therefore the call to getVal() returns the default value of s2, which is null.

You can change the order of the static variables in order to initialize s2 first:

static String s2 = "S2";
static String s1 = getVal();

Another way to force the initialization of s2 to take place before that of s1 is to make s2 final:

static String s1 = getVal();
static final String s2 = "S2";

Following is an excerpt of the initialization order as stated in JLS 12.4.2. Detailed Initialization Procedure. The sections related to static variables are highlighted.

For each class or interface C, there is a unique initialization lock LC. The mapping from C to LC is left to the discretion of the Java Virtual Machine implementation. The procedure for initializing C is then as follows:

  1. Synchronize on the initialization lock, LC, for C. This involves waiting until the current thread can acquire LC.

  2. If the Class object for C indicates that initialization is in progress for C by some other thread, then release LC and block the current thread until informed that the in-progress initialization has completed, at which time repeat this step.

  3. If the Class object for C indicates that initialization is in progress for C by the current thread, then this must be a recursive request for initialization. Release LC and complete normally.

  4. If the Class object for C indicates that C has already been initialized, then no further action is required. Release LC and complete normally.

  5. If the Class object for C is in an erroneous state, then initialization is not possible. Release LC and throw a NoClassDefFoundError.

  6. Otherwise, record the fact that initialization of the Class object for C is in progress by the current thread, and release LC.

    Then, initialize the static fields of C which are constant variables(§4.12.4, §8.3.2, §9.3.1).

  7. Next, if C is a class rather than an interface, then let SC be its superclass and let SI1, ..., SIn be all superinterfaces of C that declare at least one default method. The order of superinterfaces is given by a recursive enumeration over the superinterface hierarchy of each interface directly implemented by C (in the left-to-right order of C's implements clause). For each interface I directly implemented by C, the enumeration recurs on I's superinterfaces (in the left-to-right order of I's extends clause) before returning I.

    For each S in the list [ SC, SI1, ..., SIn ], if S has not yet been initialized, then recursively perform this entire procedure for S. If necessary, verify and prepare S first.

    If the initialization of S completes abruptly because of a thrown exception, then acquire LC, label the Class object for C as erroneous, notify all waiting threads, release LC, and complete abruptly, throwing the same exception that resulted from initializing S.

  8. Next, determine whether assertions are enabled (§14.10) for C by querying its defining class loader.

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

like image 15
Eran Avatar answered Oct 16 '22 07:10

Eran