Object Identity vs Equality — Specification Reading Guide¶
The identity-vs-equality distinction is spec-defined in three places: JLS §15.21 (equality operators), JLS §5.1.7 (boxing conversion and the integer cache), and
java.lang.Object(equals/hashCodecontracts plusSystem.identityHashCode). Addjava.lang.String.intern,java.lang.Enumfor the enum-singleton guarantee, and the bytecode-levelif_acmpeq/if_acmpneinstructions in JVMS §6.5, and you have the complete spec footprint.
1. Where to find the canonical text¶
| Concept | Authoritative source |
|---|---|
Equality operators == and != | JLS §15.21 — Equality Operators |
Numerical equality == on primitives | JLS §15.21.1 |
| Boolean equality | JLS §15.21.2 |
Reference equality == on objects | JLS §15.21.3 |
| Boxing conversion and the cached range | JLS §5.1.7 — Boxing Conversion |
| Unboxing conversion | JLS §5.1.8 |
Object.equals(Object) contract | java.lang.Object Javadoc |
Object.hashCode() contract | java.lang.Object Javadoc |
System.identityHashCode(Object) semantics | java.lang.System Javadoc |
String.intern() and the string pool | java.lang.String.intern Javadoc |
String literal interning | JLS §3.10.5 — String Literals |
| Enum singleton guarantee | JLS §8.9 — Enum Classes, java.lang.Enum |
if_acmpeq / if_acmpne bytecodes | JVMS §6.5.if_acmpeq |
| Records and structural equality | JLS §8.10 |
The JLS is the binding text for the source-level semantics; the JVMS gives the bytecode-level mechanics. The JDK class library Javadoc binds the platform-level contracts (e.g., String.intern).
2. JLS §15.21 — the equality operators¶
§15.21 splits the == / != operators into three cases based on operand types.
- §15.21.1 — Numerical Equality Operators. Both operands are numeric (primitive or autoboxable wrappers). The result follows IEEE-754 for floating point and two's-complement integer comparison for integral types. Wrappers are unboxed first if both sides are numeric.
- §15.21.2 — Boolean Equality Operators. Both operands are
boolean(orBooleanautoboxed). Result is the boolean comparison. - §15.21.3 — Reference Equality Operators. Both operands are reference types. The result is
trueif both refer to the same object or both arenull,falseotherwise. No method call. No unboxing.
The third case is the one that bites everyone:
"At run time, the result of
==istrueif the operand values are bothnullor both refer to the same object or array; otherwise, the result isfalse."
That single sentence defines identity comparison. There is no fallback to equals, no notion of "logical equality" — == is purely same instance. The compiler will accept s1 == s2 for any two String references and emit if_acmpeq (JVMS §6.5), which performs a pointer comparison.
The boxing/unboxing twist:
Integer a = 200, b = 200;
a == b; // §15.21.3 — both reference; identity comparison; false
a == 200; // §15.21.1 — RHS is primitive; a is unboxed; value comparison; true
The choice of which subsection applies is made by the compiler at compile time based on the operand types. If both sides are reference types, you get identity; if either side is primitive, the other is unboxed and you get value comparison. This is the rule that makes == so dangerous on wrappers — the answer depends on the static types of the operands, not their content.
3. JLS §5.1.7 — Boxing Conversion and the Integer cache¶
Boxing conversion (§5.1.7) is what happens when a primitive int becomes an Integer. The spec mandates a minimum cache:
"If the value
pbeing boxed istrue,false, abyte, or acharin the range