4
String s1 = "A"+"B";
String s2 = "AB";
System.out.println(s1 == s2); // true

String s1 and s2 should refer to String value "AB" in String constant pool. So one value "AB" one reference, reference comparison giving is true. (this is acceptable according to the theory I am aware of)

String st1 = "C D";
st1 += " E";
String str2 = "C D E";
System.out.println(st1 == str2); // false

String str1 and str2 both should refer to String "C D E" in String constant pool (two identical values cannot be in String pool ). Then why the reference comparison of str1 and str2 return false?

What I am missing here?

Thanks

Morteza Jalambadani
  • 2,190
  • 6
  • 21
  • 35
shanika yrs
  • 303
  • 1
  • 13
  • @AlexSalauyou st1 += " E". so st1, shouldn't it hold the reference to "C D E" in string constant pool? – shanika yrs Nov 14 '18 at 11:05
  • @AlexSalauyou pretty sure `st1 += " E"` is just the short form of `st1 = st1 + " E"` and both will produce the same bytecode and in fact change/reassign `st1` – OH GOD SPIDERS Nov 14 '18 at 11:06
  • @OHGODSPIDERS shame on me ) thanks – Alex Salauyou Nov 14 '18 at 11:07
  • 6
    The reason is that in first case "A" + "B" is evaluated to constant "AB" in compile time, thus result is taken from the pool on instance initialization. In second case, you perform operation in code flow. There is no guarantee that equivalent Strings are same object until you adjust them calling `String#intern` – Alex Salauyou Nov 14 '18 at 11:08
  • @AlexSalauyou it will be a great help if you can elaborate the mechanism or give some reference to read. thanks! – shanika yrs Nov 14 '18 at 11:11
  • 1
    https://docs.oracle.com/javase/specs/jls/se7/html/jls-3.html#jls-3.10.5 – Alex Salauyou Nov 14 '18 at 11:12
  • @AlexSalauyou yet can there be two copies of a same distinct value reside in String constant pool? as how I understand now in the runtime "C D E" is in string constant pool with two different references. – shanika yrs Nov 14 '18 at 11:21
  • @shanikayrs your preposition is that **every** String instance resides in constant pool, but it is not true – Alex Salauyou Nov 14 '18 at 11:43
  • That (underrated) answer https://stackoverflow.com/a/29655121/1531124 ... says it in simple words: *Java creates a single immutable object per string, but will reuse **literals** for performance gains.* – GhostCat Nov 14 '18 at 11:49
  • @GhostCat "A" + "B" is a constant expression, following rules for literals – Alex Salauyou Nov 14 '18 at 11:50
  • @AlexSalauyou Touche, that example doesnt work. – GhostCat Nov 14 '18 at 11:55

3 Answers3

3

The answer is very simple.

When you type String st1 += " E" or String st1 = st1 + " E" JVM transforms it to the StringBuilder syntax like String st1 = new StringBuilder(st1).append(" E").toString(). When you look at StringBuilder.toString():

@Override
public String toString() {
    // Create a copy, don't share the array
    return new String(value, 0, count);
}

... you see new String(), it creates new string not using constants from String Pool.

Finally, if you replace st1 += " E" to s1 = (s1 + " E").intern(), you get true as results.

P.S.

If I remember correctly, this is valid from JVM 6 or so. That's why now you can use string concatenation str = str1 + str2 (internally this is str = new StringBuilder(str1).append(str2).toString()). But it is not true, when you use it inside loop:

String str = "A";

for(int i = 1; i <= 3; i++)
    str += i;

JVM cannot optimize it to use StringBuilder and does old-style concatenation with generating multiple temporary strings, which is very slowly.

Oleg Cherednik
  • 17,377
  • 4
  • 21
  • 35
1

The rule you mentioned regards string literals, i.e. pieces of code written in double quotes, like "AB". Result of operation, which may be evaluated on compile time ("constant expression"), is immediately replaced by resulting literal:

String st1 = "A" + "B";

is the same as writing:

String st1 = "AB";

And equal literals are always evaluated to same String object, as told in specification (https://docs.oracle.com/javase/specs/jls/se7/html/jls-3.html#jls-3.10.5)

Moreover, a string literal always refers to the same instance of class String. This is because string literals - or, more generally, strings that are the values of constant expressions (§15.28) - are "interned"

In your question, you assume that constant pool cannot hold two different instances of the same value, that is right, but that does not imply that every String instance resides in constant pool. In fact, there is no possibility to guaranteely make equal strings created on runtime be the same object, for example:

String st1 = "AB";
String st2 = new String(st1);
String st3 = new String(st1);

will result in three different String instances: first as literal evaluation result, taken from constant pool, others as newly constructed objects (though new always creates a new object!). == on them will fail unless you adjust them using String#intern:

st1 == st2.intern();  // true
Alex Salauyou
  • 14,185
  • 5
  • 45
  • 67
1

In addition to Alex Salauyou and oleg.cherednik answers: In bytecode below you can see, that string s1 was evaluated on compile time as Alex Salauyou told, but the second string was constructed with StringBuilder as oleg.cherednik explained.

Code:
   0: ldc           #2                  // String AB <- s1 evaluated on compile time there
   2: astore_1
   3: ldc           #2                  // String AB
   5: astore_2
   6: getstatic     #3                  // Field java/lang/System.out:Ljava/io/PrintStream;
   9: aload_1
  10: aload_2
  11: if_acmpne     18
  14: iconst_1
  15: goto          19
  18: iconst_0
  19: invokevirtual #4                  // Method java/io/PrintStream.println:(Z)V
  22: ldc           #5                  // String C D
  24: astore_3
  25: new           #6                  // class java/lang/StringBuilder
  28: dup
  29: invokespecial #7                  // Method java/lang/StringBuilder."<init>":()V
  32: aload_3
  33: invokevirtual #8                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
  36: ldc           #9                  // String  E
  38: invokevirtual #8                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
  41: invokevirtual #10                 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
  44: astore_3
  45: ldc           #11                 // String C D E
  47: astore        4
  49: getstatic     #3                  // Field java/lang/System.out:Ljava/io/PrintStream;
  52: aload_3
  53: aload         4
  55: if_acmpne     62
  58: iconst_1
  59: goto          63
  62: iconst_0
  63: invokevirtual #4                  // Method java/io/PrintStream.println:(Z)V
  66: return