3

Class A contains:

  • static Strings
  • a static Map that is populated in the class's static block
  • a static method, say getStr(), that returns a String built from the static Strings

Class B contains:

  • a static Map
  • a static block that populates the Map using A.getStr()

Will B's static block always successfully populate its map? I'm assuming that the JVM is able to handle this. Could there be any issue with regards to the order in which the static blocks are executed when the classes are loaded, which in turn could not guarantee B populating the map correctly? I'd like to be certain, and perhaps understand the mechanism behind this.

foamroll
  • 752
  • 7
  • 23
  • Why should it not be successfully populated? And more importantly, why only *sometimes*? – Darkhogg Sep 30 '15 at 21:53
  • 2
    It might not be successfully populated if there is a cyclical dependency in the initialization. – Andy Turner Sep 30 '15 at 21:55
  • @mastov - if that is so, then is it true that there is no guarantee that B will always populate successfully? – foamroll Sep 30 '15 at 21:59
  • Where is there a cyclical dependency there? I see B relying on A, but not the other way around, unless you mean to say that A's Map is reliant on B's static block (if so, could use some clarification). – childofsoong Sep 30 '15 at 22:10
  • There isn't enough information here to verify that there isn't a cyclical dependency: we don't have the implementations of the classes and all of their dependencies. So, the cautious response is: *no, it will not* always *be successfully populated; one reason they might not be so is* cyclical dependency. – Andy Turner Sep 30 '15 at 22:14

1 Answers1

2

If two classes have circular dependency during static initialization, things get tricky. Java does have a strictly defined initialization procedure, and back off when recursion is detected. However it will depend on which class initialization is triggered first at runtime. This means a partial initialized class could be visible (in your case, seeing a null getStr).

Circular dependency is not only confusing, it may even cause deadlock, if two classes are initialized from two different threads. Therefore it should be avoided at all cost. A circular dependency between two parties can always be resolved by introducing a 3rd party.

Without circular dependency, a class is always seen fully initialized; in your case, when B calls A.getStr(), it must be the case that A is fully initialized, and getStr() returns the desired value.


As an exmaple of circular dependency, suppose class A extends B.

If B is initialized first (e.g. by someone calling B.something), there is no problem; during B's initialization, it encounters A.getStr, this will trigger A's initialization; when that is done, A.getStr is then executed, and sees a properly initialized field.

However, if A is initialized first, there will be trouble; it will trigger superclass B's initialization; when B invoked A.getStr, VM sees that A is already in the process of initialization, so it backs off and won't try to finished A's initialization; A.getStr will simply see null.

To break this circle, we can move stuff in A that B depends on to A'. Both A and B will depend on A'.

ZhongYu
  • 19,446
  • 5
  • 33
  • 61
  • 1
    Thanks for the answer. I also found this one helpful: http://stackoverflow.com/questions/30192250/how-are-object-dependencies-between-static-blocks-resolved – foamroll Sep 30 '15 at 22:10