This occurs because of string interning in Java.
I will try to explain this using a simpler example. Please let me know if you need elaboration.
String a = "hello"; // "hello" created in object pool
String b = "hello"; // "hello" found in object pool, b references the same string object
System.out.println(a == b); // prints true
This is because when you assign a literal to a String, "hello" is stored in the string literal pool. Anytime you use the literal "hello" it points to the same location in the pool. If you use a constructor however you will get false, because the String objects will have different memory addresses.
A 'safer' way to compare strings is using the equals()
method. If you want to avoid interning from occuring, instead of initializing a string like this String a = "hello";
, do this String a = new String("hello");
The second approach forces a new string object to be created.
Another note. Using constructors does ensure that two separate declarations are made. So, now we have two separate objects in the pool that contain the string "hello" But, calling the intern() function ensures that there is only one copy of "hello" in the pool stack and that both the objects point to it. So, again, an equality check would yield true.
Another small note. This part concerns using .equals()
et al., over ==. Post interning, of course, == is ‘faster’ than equals() [since you are checking pointer references in == vs you are comparing string based on characters in .equals()
]. So, if you need speed, you can compare strings by reference (== is faster than equals) (interning takes time though). Obviously, there are multiple side effects to this, which might not be within the scope of this post.