Value of static variable not changed even after initializing the child class in Java

Viewed 2965

When I invoke the static variable y by using Checks.y (Checks being a subclass), the static block is not executed and the value of y doesn't get updated.

class Par {
    static int y = 4;
}

class Checks extends Par {
    static {
        y = 5;
    }
}

public class Check {
    public static void main(String args[]) {
        System.out.println(Checks.y); // here printing 4
    }
}

As static is shared among all subclasses, the value is supposed to be updated.

What could be the reason behind it?

8 Answers

The field y is not declared by class Checks.

Reading static fields doesn't trigger initialization of the referenced class (Checks), unless that class is the one in which the field is declared (see JLS quote below). In this example, even if y is accessed through Checks, that will only trigger the initialization of Par because Par is the class declaring y.

In other words, the class Checks is in a sense not used at runtime.

This is perhaps one illustration of why it's wrong to access static members through subclasses, something that causes a compile-time warning.


There's a simple explanation in the specification:

12.4.1. When Initialization Occurs

A class or interface type T will be initialized immediately before the first occurrence of any one of the following:

  • T is a class and an instance of T is created.

  • A static method declared by T is invoked.

  • A static field declared by T is assigned.

  • A static field declared by T is used and the field is not a constant variable (§4.12.4).

  • T is a top level class (§7.6) and an assert statement (§14.10) lexically nested within T (§8.1.3) is executed.
    ...
    A reference to a static field (§8.3.1.1) causes initialization of only the class or interface that actually declares it, even though it might be referred to through the name of a subclass, a subinterface, or a class that implements an interface.

The last note explains why your subclass is not being initialized.

From JLS 12.4.1:

A class or interface type T will be initialized immediately before the first occurrence of any one of the following:

  • T is a class and an instance of T is created.
  • T is a class and a static method declared by T is invoked.
  • A static field declared by T is assigned.
  • A static field declared by T is used and the field is not a constant variable (§4.12.4).
  • T is a top level class (§7.6), and an assert statement (§14.10) lexically nested within T (§8.1.3) is executed.

Since y is not declared in checks, none of the above criteria is satisfied.

Another way to illustrate this behavior:

class par {
    static int y = 4;
    static {
        System.out.println("static constructor of par");
    }
}

class checks extends par {
    static int x = 6;
    static {
        System.out.println("checks static constructor");
        y = 5;
    }
}

public class check{
    public static void main(String args[]){
        System.out.println(checks.y);
        System.out.println(checks.x);
        System.out.println(checks.y);
    }
}

Output

static constructor of par
4
checks static constructor
6
5

So after invoking checks.x that satisfies the second rule, the static constructor gets invoked.

That's because the static block in the checks class doesn't get executed. Although you mention the class checks the JVM doesn't load it at all.

You can confirm this by adding another System.out.println inside the static block.

class checks extends par {

    static {
        System.out.println("Test");
        y = 5;
    }
}

The word Test will never get printed.


Read the section 12.4.1. When Initialization Occurs of the Java Language Specification to know more.

Here:

System.out.println(checks.y); // Here printing 4

y refers to a field of the par class. This field access results into the loading of the par class (the parent class) according to the JLS (emphasis is mine):

12.4. Initialization of Classes and Interfaces

....

12.4.1. When Initialization Occurs

A class or interface type T will be initialized immediately before the first occurrence of any one of the following: T is a class and an instance of T is created. A static method declared by T is invoked.

A static field declared by T is assigned.

A static field declared by T is used and the field is not a constant variable (§4.12.4).

T is a top level class (§7.6) and an assert statement (§14.10) lexically nested within T (§8.1.3) is executed.

But it doesn't load the checks class because (emphasis is mine) :

A reference to a static field (§8.3.1.1) causes initialization of only the class or interface that actually declares it, even though it might be referred to through the name of a subclass, a subinterface, or a class that implements an interface.

The one aspect not mentioned so far that might be confusing to new Java programmers: the fact how you organize your source code doesn't matter to a certain degree!

You have your two classes (which should be name Parent and Child btw to follow Java naming conventions) in one file, or one piece of example. So you probably assume: these things come together automatically at runtime.

But at runtime, there are individual class files per class. And as the others have said: nothing in the code references the child class. Thus that class isn't loaded, thus the assignment doesn't occur!

As others mention, the static block isn't executed because the class isn't initialized as answered before.

Notice the class convention names start with an upper case letter.

A clearer example to show Overriding class isn't used:

class Par {
    static int y = 4;
    public static void main(String args[]) {
        System.out.println(Checks.y);    // Here printing 4
        System.out.println(new Checks().y);    // Here printing 5
    }
}

class Checks extends Par {
   static {
        y = 5;
    }
}

As per your example, the class Check's static block never get called. Static blocks are always run before the object is created. If you add checks, object = new checks(), in your Check class you should see the expected value.

Here is a variant of how you force the initialization of Checks class.

class Par {
    static int y = 4;
}

class Checks extends Par {
    public static int x;
    static {
        y = 5;
    }
}

 class Check {
    public static void main(String args[]) {
        System.out.println(checks.y); // Prints 4
        System.out.println(checks.x); // Prints 0
        System.out.println(checks.y); // Prints 5
    }
}
Related