55
String a = "devender";
String b = "devender";
String c = "dev";
String d = "dev" + "ender";
String e = c + "ender";

System.out.println(a == b);     //case 1: o/p true

System.out.println(a == d);     //case 2: o/p true

System.out.println(a == e);     //case 3: o/p false

a & b both are pointing to the same String Literal in string constant pool. So true in case 1

String d = "dev" + "ender";

should be internally using something like:

String d = new StringBuilder().append("dev").append("ender").toString();

How a & d are pointing to the same reference & not a & e ?

Mikheil Zhghenti
  • 734
  • 8
  • 28
Dev
  • 13,492
  • 19
  • 81
  • 174
  • Look here http://www.javatpoint.com/string-comparison-in-java – Azat Nugusbayev Dec 29 '15 at 11:02
  • Compiler knows everything about `d` at compile time, so it can be optimized and interned. `e` uses a variable, so compiler does not know everything about it at compile time. Also, none of this behavior is guaranteed, even if it is common. – Mad Physicist Dec 29 '15 at 11:03
  • @AzatNugusbayev I followed that tutorial..:P – Dev Dec 29 '15 at 11:03
  • So, if you read this article, so, you must understand what causes your problem! There is creation of a `new String`, so that is why it refers to another pointer.(It is about java implementation of `String`) – Azat Nugusbayev Dec 29 '15 at 11:05
  • @AzatNugusbayev why new String is created in case 3 not case 2 while both are using StringBuilder? – Dev Dec 29 '15 at 11:07
  • 6
    @dev - case -2 is not using `StringBuilder`, also, making `c` `final` will make case 3 return `true` – TheLostMind Dec 29 '15 at 11:10
  • 3
    @sidgate definitely not a duplicate. – Rolf ツ Dec 29 '15 at 16:13

7 Answers7

84

Four things are going on:

  1. (You clearly know this, but for lurkers) == tests to see if the variables point to the same String object, not equivalent strings. So even if x is "foo" and y is also "foo", x == y may be true or false, depending on whether x and y refer to the same String object or different ones. That's why we use equals, not ==, to compare strings for equivalence. All of the following is just meant to explain why == is sometimes true, it's not a suggestion to use == to compare strings. :-)

  2. Equivalent string constants (strings the compiler knows are constants according to various rules in the JLS) within the same class are made to refer to the same string by the compiler (which also lists them in the class's "constant pool"). That's why a == b is true.

  3. When the class is loaded, each of its string constants is automatically interned — the JVM's string pool is checked for an equivalent string and if one is found, that String object is used (if not, the new String object for the new constant is added to the pool). So even if x is a string constant initialized in class Foo and y is a string constant initialized in class Bar, they'll be == each other.

    Points 2 and 3 above are covered in part by JLS§3.10.5. (The bit about the class constant pool is a bit of an implementation detail, hence the link to the JVM spec earlier; the JLS just speaks of interning.)

  4. The compiler does string concatenation if it's dealing with constant values, so

    String d = "dev" + "ender";
    

    is compiled to

    String d = "devender";
    

    and "devender" is a string constant the compiler and JVM apply points 2 and 3 above to. E.g., no StringBuilder is used, the concatenation happens at compile-time, not runtime. This is covered in JLS§15.28 - Constant Expressions. So a == d is true for the same reason a == b is true: They refer to the same constant string, so the compiler ensured they were referring to the same string in the class's constant pool.

    The compiler can't do that when any of the operands is not a constant, so it can't do that with:

    String e = c + "ender";
    

    ...even though code analysis could easily show that the value of c will definitely be "dev" and thus e will definitely be "devender". The specification only has the compiler do the concatenation with constant values, specifically. So since the compiler can't do it, it outputs the StringBuilder code you referred to and that work is done at runtime, creating a new String object. That string isn't automatically interned, so e ends up referring to a different String object than a does, and so a == e is false.

    Note that as Vinod said, if you declared c as final:

    final String c = "dev";
    

    Then it would be a constant variable (yes, they're really called that) and so §15.28 would apply and the compiler would turn

    String e = c + "ender";
    

    into

    String e = "devender";
    

    and a == e would also be true.

Just to reiterate: None of which means we should use == to compare strings for equivalence. :-) That's what equals is for.

Community
  • 1
  • 1
T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
18

The compiler does a lot of optimisation under the hood.

String d = "dev" + "ender";

Here the compiler will replace "dev" + "ender" with "devender" when the program is being compiled. If you are adding 2 literals (this applies to both primitives as well as Strings), the compiler does this optimisation.

Java code :

String d = "dev" + "ender";

Byte code :

  0: ldc           #16                 // String devender

Coming to a special case :

final String c = "dev"; // mark this as final
String e = c + "ender";

Making c final will make the String a compile-time-constant. The compiler will realize that the value of c cannot change and hence will replace all occurances of c with the value "dev" when compiling, thus e will be resolved during compile time itself.

TheLostMind
  • 35,966
  • 12
  • 68
  • 104
  • "The compiler does a lot of optimisation under the hood." It really doesn't. Java compilers leave that up to the JVM. The JLS requires compilers to do this *particular* replacement only as it's a constant expression. – Boann Dec 30 '15 at 14:38
  • @Boann - The compiler does those optimisations based on what the JLS specifies. I understand, the compiler leaves a lot to the JIT – TheLostMind Dec 30 '15 at 15:30
9

As you said internally the last concatenation is done to something similar to

String e = new StringBuilder().append(c).append("ender").toString();

the implementation of toString() of StringBuilder creates a new String. Here is the implementation.

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

Comparing strings using == instead of .equals() returns true only if both strings are the same. In this case they are not the same because the second string is created as a new object of type String.

The other concatenations are performed directly by the compiler so no new String is created.

Davide Lorenzo MARINO
  • 26,420
  • 4
  • 39
  • 56
9

"dev" + "ender" is a compile-time evaluable constant expression: both arguments are string literals. The expression is therefore "devender".

The same cannot be said for c + "ender": certain circumstances (some code running on a different thread for example) could lead to c being set to a different value. Qualifying c as final obviates this possibility, and in that case e would also refer to the same object as a.

So a, b, and d all refer to the same object.

Bathsheba
  • 231,907
  • 34
  • 361
  • 483
9

The difference between d and e is that when you concatenate string literals, the concatenation is performed at compile time. Java compiler treats "dev" + "ender" expression in the same way as "devender" expression, producing the same literal at compile time. Since all String literals get interned, d, which is a result of "dev" + "ender", also ends up referencing the same object as a and b's "devender".

The expression for e, which is c + "ender", is evaluated at runtime. Even though it produces the same string, this fact is not used by the compiler. That's why a different String object is produced, resulting in failed comparison on ==.

Sergey Kalinichenko
  • 714,442
  • 84
  • 1,110
  • 1,523
6

String d = "dev" + "ender"; constant + constant, ‘d’ is still a constant(the same one), so (a == d) is true;

String e = c + "ender"; variable + constant, the result 'e' is a variable, it will use StringBuilder internally,and create a new reference.

maolazhu
  • 76
  • 1
0

Keep in mind that Java holds a pool of all string literals found in program, used for matching purposes among others, so any different string literal concatenation above will lead to the same object, to the same string literal. You can check out this useful article for more.

On the other hand, the concatenation of a String object and a literal (case c + "ender") will lead to the creation of a as StringBuilder object at runtime, different to the literals found in pool.

Nick Louloudakis
  • 5,856
  • 4
  • 41
  • 54