0

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.

orbfish
  • 7,381
  • 14
  • 58
  • 75

3 Answers3

10

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)
Bozho
  • 588,226
  • 146
  • 1,060
  • 1,140
  • Do note that `intern()` puts the String on the table if it's not already there. This is perm gen space that won't be recovered by the garbage collector. Application code should very rarely if at all intern anything and never before you've used a memory profiler and found real reason to intern something at runtime. – Barend Oct 20 '11 at 21:47
  • yup, that's correct. However, I once needed to use string interning, and made a test - looped from 0 to millions, interning each string-represented number, and I didn't get a PermGen problem. Perhaps the older entries in the pool are cleaned. – Bozho Oct 20 '11 at 21:50
  • +1 - I don't have a copy of *Effective Java*, but I'm sure that it does not give immutability as the reason why `a == b` is `true`. The OP should reread the relevant section. – Stephen C Oct 20 '11 at 21:51
  • 2
    @Bozho While you're definitely right on "never use `new String("something")`," the code like `String s = hey.retrieveMeThatString(); s = new String(s);` can be found in production code and in some cases even makes sense. – alf Oct 20 '11 at 21:53
  • 3
    @alf - in what cases? There is no point in making a "defensive copy" of an immutable object. – Bozho Oct 20 '11 at 21:54
  • 4
    @Bozho memory again. If you look at `"a very long string".substring(6, 11)`, you'll see that its `value` field contains the whole original array, `[a, ,v,e,r,y, ,l,o,n,g, ,s,t,r,i,n,g]`. If you receive huge strings that you take apart in order to use parts for a long time, you don't want to keep the original objects. – alf Oct 20 '11 at 21:58
  • @alf sounds like premature optimization to me – Sean Patrick Floyd Oct 20 '11 at 22:00
  • @SeanPatrickFloyd I repeat, *"and in **some cases** even makes sense"* – alf Oct 20 '11 at 22:01
  • Yes, I know you're not supposed to do new String("x"), yes I maybe should have said instance control rather than immutability (you seemed to have gotten what I meant), yes, I understand the difference between identity and logical equality, I'm just trying to understand the details better. The question was, does anyone remember where the really good writeup on this was. But you've got the most complete non-answer so far if nothing else shows up. ;) – orbfish Oct 20 '11 at 22:48
  • I agree with alf AND bozho there. `new String(s)` is pretty useless, but `new String(s, start, end)` makes a lot of sense. Keeping a several hundred MB large file in memory when we only need a handful strings isn't a great idea and I can live with the "premature optimization" of replacing one method with another (which basically take the same arguments anyhow) ;-) Actually reusing the char array seems like premature optimization to me - I agree with the c# people who got rid of this rather surprising behavior. – Voo Oct 20 '11 at 22:57
  • 1
    Indeed, see also http://stackoverflow.com/questions/2147783/why-does-appending-to-a-string-save-memory and http://stackoverflow.com/questions/390703/what-is-the-purpose-of-the-expression-new-string-in-java – BalusC Oct 21 '11 at 02:03
  • @Bozho "However, when you expicitly create a new instance, you construct a new string object based on the string object taken from the table. " What do you mean by that ? If there is already mapping why can't the object with that mapping be returned even though the developer carelessly did a new String() ? if new String("fish").intern() rultimately eturn the same instance as simply String s = "fish" , why does it matter if a developer carelessly use the new String () ? – Geek Jul 31 '12 at 04:28
  • Ask the language designers :) I can't think of a case where you need new String(str) – Bozho Jul 31 '12 at 13:09
0

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.

Mark D
  • 919
  • 8
  • 6
0

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
alf
  • 8,377
  • 24
  • 45