1

Let's say I have a class

class A{
    public final static TreeMap<String,String> tmap = new TreeMap<>();
    int x;

    static{
        tmap.put("x:I", "Hello");
    }
}

and I create a subclass

class B extends A{
    long y;

    static{
        tmap.put("y:J","World");
    }
}

If I now write some code to check the static initialisers:

class Main{
    public static void main (String[] args){
        B b = new B();
        for(String v : b.tmap.values()){
            System.out.println(v);
        }
    }
}

I know both entries must be in tmap because A must get loaded, eventually, for B's super call at the very latest.

But if I'm reading When does static class initialization happen? correctly, I cannot assume that the Hello value is put into the map first all the times because tmap is final.

So if ordering were important (say if I knew there's a chance some values may be updated/overwritten further down the hierachy), do I need to remove the final modifier?

Or is there something else already enforcing "proper" static initialiser ordering?

User1291
  • 7,664
  • 8
  • 51
  • 108
  • In your example the order by which the enties are added doesn't really matter since a TreeMap will automatically order them by the natural order of the key. A LinkedHashMap might work better as a example for this question. – OH GOD SPIDERS Jan 23 '18 at 14:40
  • Why do you think final has anything to do with the order? – Sotirios Delimanolis Jan 23 '18 at 14:44
  • @SotiriosDelimanolis because the answer to the linked-to question emphasises that the static initialiser is called before the first use of a **non-constant** static field. static final fields *are* constant. – User1291 Jan 23 '18 at 14:54
  • If you follow the JLS link in that answer, you'll see that it means a _constant variable_. A `final` and `static` field of type `TreeMap` is not a _constant variable_. – Sotirios Delimanolis Jan 23 '18 at 14:56

1 Answers1

1

Static initialization is always done for the super class before the child is initialized, if you have multiple static initializer blocks, they are executed in order. (Edit: Thanks to Holger for pointing out that this does not have to be directly when the class is loaded).

In your example, B extends A, and the classloader therefore has to load A before B. Thus, the static initializer of A ist executed first.

This has nothing to do with the final modifier. What the other thread probably refers to is that if the compiler can perform constant folding( https://www.javaworld.com/article/2076060/build-ci-sdlc/compiler-optimizations.html), the class from which the constant is taken will not be static initialized (as the reference is replaced at compile time).

lwi
  • 1,682
  • 12
  • 21
  • You meant "the classloader has to load ``A`` before ``B``"? Otherwise your conclusion that "the static initiliser of ``A`` is executed first" isn't exactly evident. – User1291 Jan 25 '18 at 13:08
  • 1
    This is wrong. The initialization is not “done as soon as the class is loaded”. [The specification](https://docs.oracle.com/javase/specs/jls/se8/html/jls-12.html#jls-12.4.1) tells exactly, which events may trigger a class initialization. It’s perfectly possible that a loaded class’ initializer never runs. But thankfully, there’s the explicit statement, “*When a class is initialized, its superclasses are initialized (if they have not been previously initialized)…*”, so `A`’s initializer is indeed executed before `B`’s, but not because of your reasoning. – Holger Feb 20 '18 at 13:17
  • @Holger Thanks, for pointing that out. Fixed it in the answer – lwi Feb 20 '18 at 14:00