The labs on this site need JavaScript to run. The same content is written out below.
Learn › Java › Commonly used classes
== and equals
Why is == false when the values are the same?
== compares whether two variables refer to the same object, and equals compares whether the contents are the same. Depending on how they are created, strings and Integer objects are sometimes reused and sometimes created anew, so the result of == can differ even when the values are the same.
Checked against real execution · Every combination was run on JDK 21 and checked against the result
Concepts in this lab
- Identity and equality
- Identity means that two references point to the same object in memory, and you compare it with the == operator. Equality means that two objects logically have the same value, and you compare it with the equals() method. If you use == to compare strings or value objects, the result can be false even when the contents are the same.
- String pool
- This is a storage area where the JVM keeps string literals and reuses the existing object when a literal with the same contents appears again. It saves memory because the same string is not created several times. This sharing is possible because String is immutable. Creating a string with new String("a") makes a new object that is separate from the pool.
- Boxing and unboxing
- Boxing converts a primitive type (such as int) to a wrapper object (such as Integer), and unboxing is the reverse. Collections can hold only objects, so a primitive value that you add is boxed automatically. This has a cost for object creation and conversion, and unboxing a wrapper that is null throws a NullPointerException.
- Immutable object
- This is an object whose state does not change after it is created. Because its value never changes, it is safe for several threads to use at the same time, you can pass it to other code without worrying that it will be changed, and it is well suited for caching or for use as a key in a hash collection. Declare the fields private final, provide no methods that change the state, and make the class final so that a subclass cannot change its behavior. If a field refers to a mutable object (such as a List or a Date), the constructor and the getters pass copies in and out (defensive copying) so that outside code cannot change the internals.
Try these
- Default settings — A literal and new String. The contents are the same, but == is false.
- Two literals — Identical literals use the same object in the string pool.
- A string concatenated with a variable — A string created at run time is a different object from the one in the pool.
- Calling intern() — You get back the object in the pool, so == becomes true.
- Integer 127 — == is true.
- Integer 128 — The value is only 1 larger, but == becomes false.
- Comparing an Integer with an int — When one side is a primitive type, the values are compared.
Running with the default settings
- String a = "hello": Only one copy of a string literal is created, and it is kept in the string pool. a refers to the "hello" object in the pool.
- String b = new String("hello"): new always creates a new object. Its contents are the same as the "hello" in the pool, but it is a separate object at a different place on the heap.
- a == b: This compares whether the two variables refer to the same object. a refers to object 1 and b refers to object 2, so the result is false.
- a.equals(b): This compares the values inside the objects. The contents are the same, so the result is true.
Result of this run
- The contents are the same, but the two are different objects, so == is false.
- Always use equals to compare the contents of strings. A string read from a database or from a request is not a literal, so comparing it with == gives false in most cases.
Code
String a = "hello";
String b = new String("hello");
System.out.println(a == b);
System.out.println(a.equals(b));
Assumptions of this model
The == result of every combination was checked against the result of running the same code on JDK 21 on this PC. The Integer cache range of -128 to 127 is the minimum range that the Java Language Specification guarantees, and the upper bound can be raised with a JVM option. The string pool has been located inside the heap since JDK 7, but the diagram shows it separately to set it apart.
Previous lab: Strategy pattern · Next lab: equals and hashCode