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