Basically, it is an optimization to save memory.
On your IDE inside Integer class see IntegerCache static nested class, there you will find how is this implemented.
Here you will find detailed explanations:
Warning: A pure speculation.
It seems when <i1> and <i2> are assigned a new value, a new object is created for each these variables, and then, having each their own object, the == operator returns False. Because the variables <i1> and <i2> no longer reference to the same object.
System.identityHashCode(i1) differs to System.identityHashCode(i2). After the new value assignment.
However, I read in below discussion, System.identityHashCode() isn't something we should rely on for comparison of objects. This is not something comparable to the `address of` & operator in C/C++ I suppose.