Why immutable new String(“fish”) != new String(“fish”)?
-
10-02-2021 - |
Question
I remember reading a section, possibly in Bloch's Effective Java, that said that for most cases, where
String a = "fish";
String b = "fish";
that a == b in most cases because Strings are immutable. But that due to temporary construction of objects or some such, new String("fish") would yield a distinct object reference.
I looked through Bloch chapters on equals(), immutability, and object creation, but cannot find this bit I remember!! Tearing my hair out, does anyone remember where is the description of why this is? It may not even be in EJ but I'd like to find it. Hint: where is this explained is my actual question.
Solution
It's not related to immutability. It's the way strings are handled by the JVM. A string literal with the same contents represents the same object ("string literal" means roughly "text surrounded by quotes"). There is a table of string objects in the JVM, and each string literal has exactly one object in that table.
However, when you expicitly create a new instance, you construct a new string object based on the string object taken from the table.
From any string formed by not using a literal (but by calling toString(), by instantiating, etc.) you can get the object from the jvm table by calling str.intern()
. The intern()
method returns exactly one instance for each character sequence that exists. new String("fish").intern()
will return the same instance as simply String s = "fish"
There are two things to remember:
- never use
new String("something")
- always compare strings with
equals(..)
(unless you really know what you are doing, and document it)
OTHER TIPS
I think you are looking for String.intern() method which maintains a constant string pool.
The operator '==' compares object references (addresses) while .equals() is a method call which looks at semantic equivalence.
The compiler will look at String a = "fish" and String b = "fish" and then may or may not point to the same address. However, if you do a.intern(); b.intern() then it will probably put them in the same string pool and a == b.
If you're looking for a definitive description, well, go to the definition: JLS § 3.10.5 String Literals.
The example code you should be familiar with is,
Thus, the test program consisting of the compilation unit (§7.3):
package testPackage; class Test { public static void main(String[] args) { String hello = "Hello", lo = "lo"; System.out.print((hello == "Hello") + " "); System.out.print((Other.hello == hello) + " "); System.out.print((other.Other.hello == hello) + " "); System.out.print((hello == ("Hel"+"lo")) + " "); System.out.print((hello == ("Hel"+lo)) + " "); System.out.println(hello == ("Hel"+lo).intern()); } } class Other { static String hello = "Hello"; }
and the compilation unit:
package other; public class Other { static String hello = "Hello"; }
produces the output:
true true true true false true