Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How are object dependencies between static blocks resolved?

I recently came across this at work. While I am not sure it is really a good idea, I don't understand how static blocks are handled by the compiler.

Here is an example:

Consider that you have classes A and B:

public class A {

    public final static List<Integer> list;
    static {
        list = new ArrayList<>();
    }
}

public class B {

    public final static int dependsOnA;
    static {
        dependsOnA = A.list.size();
    }
}

And a main class that just reads B.dependsOnA.

The static block in B is dependent of the one in A, since it uses the list static variable.

Now, the code executes properly and no NullPointerException is raised at runtime. But what is the mechanism that ensures that list is initialized before it is potentially used elsewhere?

like image 695
nsanglar Avatar asked May 12 '15 13:05

nsanglar


People also ask

How does a static block work?

Unlike C++, Java supports a special block, called a static block (also called static clause) that can be used for static initialization of a class. This code inside the static block is executed only once: the first time the class is loaded into memory.

Which will execute first static block or static variable?

There is an order in which static block/method/variable gets initialized. Static Blocks are called even before the main method which is nothing but a static method i.e. execution point of every class.

How does static block works in Java?

In a Java class, a static block is a set of instructions that is run only once when a class is loaded into memory. A static block is also called a static initialization block. This is because it is an option for initializing or setting up the class at run-time.

How do static blocks get executed if there are multiple static blocks?

A class can have any number of static initialization blocks that will execute in the same sequence as written in the program. That is, the order of execution of multiple static initialization blocks is executed automatically from top to bottom during the dot class file loading.


3 Answers

The mechanism is described in detail here, but the five most important points are:

  1. Before a class is referenced, it needs to be initialised.
  2. If initialisation of a class has already begun (or if it's finished), it isn't attempted again.
  3. Before a class is initialised, all its superclasses and superinterfaces need to be initialised first.
  4. Static initialisers within a single class are executed in textual order.
  5. Implemented interfaces are initialised in the order in which they appear in the implements clause.

These rules completely define the order in which static blocks are executed.

Your case is rather simple: before you access B.dependsOnA, B needs to be initialised (rule 1), the static initialiser is then trying to access A.list, which triggers the initialisation of class A (again rule 1).

Note that there's nothing stopping you from creating circular dependencies this way, which will cause interesting things to happen:

public class Bar {
    public static int X = Foo.X+1;

    public static void main(String[] args) {
        System.out.println( Bar.X+" "+Foo.X); // 
    }

}

class Foo {
    public static int X = Bar.X+1;
}

The result here is 2 1 because the way the initialisation happens is this:

  1. Bars initialisation begins.
  2. Bar.Xs initial value is evaluated, which requires initialising Foo first
  3. Foos initialisation begins.
  4. Foo.Xs initial value is evaluated, but since Bars initialisation is already in progress, it isn't initialised again, Bar.Xs "current" value is used, which is 0, thus Foo.X is initialised to 1.
  5. We're back to evaluating Bar.Xs value, Foo.X is 1 so Bar.X becomes 2.

This works even if both fields were declared final.

The moral of the story is to be careful with static initialisers referring to other classes in the same library or application (referring to classes in a third party library or standard class library is safe as they won't be referring back to your class).

like image 81
biziclop Avatar answered Oct 09 '22 03:10

biziclop


The "mechanism" is the JVM's classloader, which will ensure that a class' initalization blocks are executed (with a global lock across the whole JVM) before returning control flow to where the class was first referenced. It will first load class A only after referenced, in this case when B's init block references A.list.

like image 9
Grundlefleck Avatar answered Oct 09 '22 02:10

Grundlefleck


During execution of the static block of B, the runtime encounters A for the first time, and it will invoke the static block of A before it accesses A.list.

like image 8
Glorfindel Avatar answered Oct 09 '22 03:10

Glorfindel