The code flow is as follows:
1. The compiler evaluates Integer i1 = 1; to Integer i1 = Integer.valueOf(1);
2. The Integer.valueOf() method is caching the values between -128 and 127, hence no new object is created.
3. Integer i2 = new Integer(1); here, a new Integer type object is created, and it's reference is assigned to i2
4. Integer i3 = Integer.valueOf(1); according to p1 and p2 - this statement is equivalent to Integer i1 = 1;, no new object is created, the value being taken from cache, hence i3 and i1 are referencing the same (cached) object.
Consequently, i1 == i2 will return false, since i1 references a cached object, while i2 - a newly created object. Finally, i1 == i3 will return true, since both variables are referencing the same cached value.