Detailed explanation
With the usual Integer cache configuration, this code prints true followed by false, each on a separate line. The important qualification is that false for the second line is not guaranteed across all JVM configurations: an implementation may cache 128 as well. Both comparisons use == between two Integer references, so Java checks whether the operands refer to the same object, not whether those objects contain equal numeric values.
The assignments use autoboxing, which converts primitive int values into Integer objects. The Java compiler normally implements these assignments through Integer.valueOf(int). That method always caches values from -128 through 127 inclusive and may cache additional values. Consequently, a and b refer to the same cached object. With the usual cache upper bound of 127, separate calls for 128 create distinct objects, so c and d refer to different objects. In OpenJDK, this cache is maintained by the internal Integer.IntegerCache class. HotSpot allows its upper bound to be increased with -XX:AutoBoxCacheMax=<n>; if the configured cache includes 128, both lines print true.
For numeric equality between Integer objects, c.equals(d) returns true regardless of whether the objects share an identity. Objects.equals(c, d) also returns true here and handles null references safely. Explicitly comparing c.intValue() and d.intValue() works for non-null operands, but throws NullPointerException if either reference is null. If one operand is a primitive int, such as c == 128, Java unboxes the Integer and performs a numeric comparison instead. That comparison is true here, but the same null risk applies.
Interviewers use this question to test whether you distinguish primitives, wrapper objects, autoboxing, and reference identity. A strong answer gives the expected default output first, then explains caching and its configuration-dependent boundary. The language guarantees shared boxing identity for the relevant constant values in the required small range; it does not require different identities outside that range. Therefore, do not build application logic around wrapper identity: use value equality for numbers and == only when primitive equality or intentional reference identity is required.
Key points to remember
- The usual output is true, then false; an extended Integer cache can make both lines print true.
- With two Integer operands, == compares reference identity without unboxing.
- Integer.valueOf caches -128 through 127 inclusive and may cache additional values.
- Use equals() for Integer value equality or Objects.equals() when nulls are possible.
- Comparing an Integer with an int triggers unboxing, which can throw NullPointerException.
Common mistakes candidates make
- Claiming that the second line must always print false because 128 can never be cached.
- Assuming == compares numeric values whenever both objects are numeric wrappers.
- Recommending explicit unboxing or instance equals() without considering null operands.
Likely follow-up questions
What happens if the Integer cache includes 128?
Both comparisons print true because each pair refers to a shared cached object. On HotSpot, -XX:AutoBoxCacheMax=128 can extend the cache to include that value.
What does new Integer(127) == new Integer(127) produce?
It produces false because each new expression creates a distinct object rather than using the cache. This constructor has been deprecated since Java 9; prefer autoboxing or Integer.valueOf.
Does Objects.equals consider Integer.valueOf(128) and Long.valueOf(128) equal?
No: Integer.equals requires the other object to be an Integer, so Objects.equals returns false for these different wrapper types. For cross-type numeric comparison, deliberately convert to a suitable common numeric representation.
Interview tip: State the usual output, explain reference identity and autoboxing, then add the extended-cache caveat and a null-safe comparison alternative.
