views:

196

answers:

7
  String s1 = "andrei";
  String s2 = "andrei";

  String s3 = s2.toString();

  System.out.println((s1==s2) + " " + (s2==s3));

Giving the following code why is the second comparison s2 == s3 true ? What is actually s2.toString() returning ? Where is actually located (s2.toString()) ?

+6  A: 

First of all String.toString is a no-op:

/**
 * This object (which is already a string!) is itself returned.
 *
 * @return  the string itself.
 */
public String toString() {
    return this;
}

Second of all, String constants are interned so s1 and s2 are behind the scenes changed to be the same String instance.

Robert Munteanu
Shouldn't that be "String constants are interned"? I doubt that Strings coming from a file, or user input, are interned.
extraneon
As you mention that, strings coming from various inputs MAY be interned, if they hang around long enough. This uncertainty is why its usually preferable to use .equals.
mezmo
@extraneon: That's correct, I updated my answer.
Robert Munteanu
@mezmo: Correct, one should always use `equals` to compare Strings. Do you have a reference for the Strings which are interned "if they hang around long enough"?
Robert Munteanu
+1  A: 

Given that == compares references, you can see from s2 == s3 being true that s2.toString() returns s2.

Thomas Lötzer
+5  A: 

The method String.intern() can be used to ensure that equal strings have equal references. String constants are interned, so s1 and s2 will reference the same string. String.toString() simply returns itself, that is, a.toString() returns a, when a is a String. So, s2 also == s3.

In general, strings should not be compared by reference equality, but by value equality, using equals(). The reason is that it's easy to get two strings that are equivalent but different references. For example, when creating substrings. An exception to this rule is that if you know both strings have been interned beforehand (or you intern them as part of the comparison.)

To answer your implied question about heap or stack, Strings are allocated on the heap. Even if they were allocated on the stack, such as with the upcoming escape analysis and stack allocation, the semantics of the program will not change, and you will get the same result for both heap and stack allocation.

mdma
+1  A: 

You should be using .equals and not == when comparing java strings.

== Compares references and therefore s2.ToString() returns s2.

From the java glossary about heap / stack:

In Sun’s JVM, the interned Strings (which includes String literals) are

stored in a special pool of RAM called the perm gen, where the JVM also loads classes and stores natively compiled code. However, the intered Strings behave no differently than had they been stored in the ordinary object heap.

JonH
This was an interview question, i usually use equals.
Andrei Ciobanu
@Andrei Ciobanu - It figures, the questions these people ask, totally trick programmers.
JonH
A: 

s2.toString () is returning a String representation. Since it's already a String it returns itself (thats why the comparison is true).

All Strings are allocated on the Heap, the coparison operator just compares whether they are the same object (thats why s1 != s2).

nob
actually s1==s2 is true.
Andrei Ciobanu
Ah, ok, then I think java optimized them, since both refer to the same value.
nob
String s1 = "a"; String s2 = "a" - s1 == s2 is TRUE , AND FOR:String s1 = new String("a"); String s2 = new String("a"); s1 == s2 is FALSE
Andrei Ciobanu
Yep, you are right, also see Sting literal pools http://stackoverflow.com/questions/372547/where-do-java-and-net-string-literals-reside
nob
+1  A: 

Since String are immutables, a useful implementation of the toString method is - in the String class - to return this.

As an example, my rt.jar contains the following implementation :

public String toString() {
return this;
}

As a consequence, the reference associated to s3 is the same than the one associated to s2.

Considering the s1==s2 statement, it is due to the automatic call to intern() for all constant Strings. That means that at compile time, s2 initialization code will be replaced by s2=s1, which makes the assertion quite obvious, no ?

Riduidel
uhm, is it an automatic call to intern() or is it at compile time?
unbeli
I tend to think it's compile-time code modification.
Riduidel
+1  A: 

From the java virtual machine spec:

String literals and, more generally, strings that are the values of constant expressions are "interned" so as to share unique instances, using the method String.intern.

"andrei" is a String literal and therefore "interned". Therefore both String s1and s2 refer to the same String object, an interned String with the content "andrei".

Therefore (s1 == s2) && (s1.equals(s2)) is true. String#toString() doesn't create a new String (like many other methods from String) but simple returns this. Therfore s2 == s3 is true.

Andreas_D