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'.