From docs:
As with instance methods and variables, an inner class is associated
with an instance of its enclosing class and has direct access to that
object's methods and fields.
![enter image description here]()
Now taking the second example first, when i is declared in parent class, the inner class can access it, because inner class has access to entire parent object. And i is correctly referenced as Main.this.i from inner class.Now compiler secretly makes a copy of this.i inside inner class. this never changes inside the object, this.i will point to correct object for both inner and outer class. Compiler is happy.
In first example, i is not part of parent class, its declared inside a method hence its reference is now on stack, and not on heap. (In this case, i doesn't get to live on that big outer circle shown in above diagram). Now compiler must secretly make a copy of i inside inner class as well. But it is afraid that method's i may change on stack. Compiler can't use outer class connection to get to i here, and it can't bother to copy i reference from stack every time it changes. So, it is decided that you must make the method's i reference unchangeable, or in other words, final.
The obscure secret tricks Java plays so that you can access outer fields from inner classes is explained in detail here: http://techtracer.com/2008/04/14/mystery-of-accessibility-in-local-inner-classes/
and discussed more "why's" here : http://people.csail.mit.edu/gregs/ll1-discuss-archive-html/msg04044.html
tl;dr: Instance fields are directly accessible by inner classes, local fields must be made final to be accessible by a inner class.