JLS 126.96.36.199 (Restrictions on the use of Fields during Initialization) says that:
"The declaration of a member needs to appear textually before it is used only if the member is an instance (respectively static) field of a class or interface C and all of the following conditions hold:
* The usage occurs in an instance (respectively static) variable initializer of C or in an instance (respectively static) initializer of C.
* The usage is not on the left hand side of an assignment.
* The usage is via a simple name.
* C is the innermost class or interface enclosing the usage."
1) In the first of the two examples we have that the usage of obj inside the anonymous Runner cannot be flagged as illegal, since such usage appears within the scope of a class that is different wrt the class that defines obj (Huh). As such, the first bullet does not hold, and thus no compiler error should be raised. On the other hand we have that the usage of r within the initializer of obj is perfectly legal, as r's declaration is *textually before* obj's declaration.
2) in this smaller example we have a clear forward reference in the first of the two asignments (usage of b in a's initializer). The second error message cannot be justified by the JLS as, again b's initializer is referring to a, whose declaration precedes b's one (so 188.8.131.52 does not apply).
In the remaining of this section we'll focus on 2), as it is simpler (yet producing an equivalent erroneous behaviour).
The spourious forward ref error is caused by the way in which javac attributes constants (aka final fields). The initializers of such fields are in fact attributed lazily (on-demand). This means that, when we encounter the two declarations:
final int a = b;
final int b = a;
the evaluation of the initializer of a is actually triggering the evaluation of the initializer of b, since b is final. In other words, it's like if javac performs one big attribution for both initializers instead of attributing each initializer separately - since b's attribution occur within a's attribution. The consequence of this is that when javac attributes b's initializer, the reference to a is recognized as a self-reference (since we are still within a's attribution) and thus marked as erroneous.
In order to preserve lazy attribution of final variable's initializers, javac should detect self-reference in initializers in a slightly different way. In particular at any given time during attribution only one variable symbol at a time can be the cause of a forward ref error because of self-referencing. That is, when javac tries to attribute b's initializer within a's attribution, javac should only complain about self-references involving b (the variable whose initializer is currently being attributed).