Search code examples
javastaticinitializationfinal

Subclass final static initialization prior to superclass class initialization?


I couldn't find any specific SO post referring to this specific case, so I'd like to ask what I think is a yes/no question.

Here's JLS §12.4.2 (Java SE 8), listing 6-7:

  1. [...] Then, initialize the static fields of C which are constant variables (§4.12.4, §8.3.2, §9.3.1).
  2. Next, if C is a class rather than an interface, and its superclass has not yet been initialized, then let SC be its superclass [...] For each S in the list [ SC, SI1, ..., SIn ], recursively perform this entire procedure for S. If necessary, verify and prepare S first. [...]

My question: Does it mean that final static variable of a subclass is initialized before the static initialization of the superclass (assuming the final static is initialized as part of its declaration)?


Solution

  • The answer is it might be so. The key part is a constant word meaning.

    Consider this code

    class Foo {
        public static final int FOO_VALUE = 1000;
    
        static {
            System.err.println("FOO Bar.BAR_VALUE=" + Bar.BAR_VALUE);
        }
    }
    
    class Bar extends Foo {
        public static final int BAR_VALUE = 2000;
    
        static {
            System.err.println("BAR Foo.FOO_VALUE=" + Foo.FOO_VALUE);
        }
    }
    

    The output of this program will be

    FOO Bar.BAR_VALUE=2000
    BAR Foo.FOO_VALUE=1000
    

    In this case Bar static final variables are initialized before Foo static initialization. Both FOO_VALUE and BAR_VALUE are constants so javac may inline this fields.

    But you can trick compiler by pretending that final variable is not a constant in this way

    class Foo {
        public static final int FOO_VALUE = Integer.valueOf(1000).intValue();
    
        static {
            System.err.println("FOO " + Bar.BAR_VALUE);
        }
    }
    
    class Bar extends Foo {
        public static final int BAR_VALUE =  Integer.valueOf(2000).intValue();
    
        static {
            System.err.println("BAR " + Foo.FOO_VALUE);
        }
    }
    

    And output will be

    FOO Bar.BAR_VALUE=0
    BAR Foo.FOO_VALUE=1000
    

    So Foo static initializer is finished before static final Bar.BAR_VALUE was initialized.