6

I run the following code and get the results shown in the comments. I know the differences between == and .equals(). What I don't understand is why my code in the second line has different results from the code in the third line.

    String de = "de";
//  String abcde = "abc" + "de"; // abcde == "abcde" reture true
    String abcde = "abc" + de;   // abcde == "abcde" reture false;
    System.out.println();
    System.out.println(abcde=="abcde");
    System.out.println(de=="de");

In trying to debug this I used the javap -c command and got the following output 'code' for the first string concatenation:

         Code:
 0:   ldc     #9; //String de
 2:   astore_1
 3:   new     #10; //class java/lang/StringBuilder
 6:   dup
 7:   invokespecial   #11; //Method java/lang/StringBuilder."<init>":()V
 10:  ldc     #4; //String abc
 12:  invokevirtual   #12; //Method java/lang/StringBuilder.append:(Ljava/lang
 String;)Ljava/lang/StringBuilder;
 15:  aload_1
 16:  invokevirtual   #12; //Method java/lang/StringBuilder.append:(Ljava/lang
 String;)Ljava/lang/StringBuilder;
   19:  invokevirtual   #13; //Method java/lang/StringBuilder.toString:()Ljava/l
 ng/String;
   22:  astore_2
   23:  getstatic       #14; //Field java/lang/System.out:Ljava/io/PrintStream;
   26:  invokevirtual   #15; //Method java/io/PrintStream.println:()V
   29:  getstatic       #14; //Field java/lang/System.out:Ljava/io/PrintStream;
   32:  aload_2
   33:  ldc     #16; //String abcde
   35:  if_acmpne       42
   38:  iconst_1
   39:  goto    43
   42:  iconst_0
   43:  invokevirtual   #17; //Method java/io/PrintStream.println:(Z)V
   46:  getstatic       #14; //Field java/lang/System.out:Ljava/io/PrintStream;
   49:  aload_1
   50:  ldc     #9; //String de
   52:  if_acmpne       59
   55:  iconst_1
   56:  goto    60
   59:  iconst_0
   60:  invokevirtual   #17; //Method java/io/PrintStream.println:(Z)V
   63:  return

And the output for the second string concatenation:

I am not so familiar with this 'code' and can't see any reason why these differences exists. So could anyone explain why those differences happen?

Related post

Community
  • 1
  • 1
Tony
  • 5,972
  • 2
  • 39
  • 58
  • 8
    What's the actual question here? (I assume you already know that `==` compares string *references*, not string *contents*)? – NPE Jun 23 '14 at 11:14
  • 1
    Please see `String.equals(....)` method, it's NOT the same as `==` operator... – vikingsteve Jun 23 '14 at 11:15
  • 1
    To understand "Those code"...you can refer [JVM instruction set document](http://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html). – Not a bug Jun 23 '14 at 11:18
  • The javap command disassembles a class file. – SparkOn Jun 23 '14 at 11:19
  • I know the differences between == and equals(), see my edit and sorry to mislead. – Tony Jun 23 '14 at 11:24
  • @ThomasJungblut it is different from that question. – Tony Jun 23 '14 at 11:31
  • @NPE Sorry to mislead and see my edits. – Tony Jun 23 '14 at 11:31
  • 1
    JVM optimizes while instantiating string literals, i.e. if the string literal already exists in the _String Literal Pool_, it returns the same reference every time. So, all the literals having value "abcde" point to the same memory location. "==" only compares references, and since the memory location for all "abcde" is same, it returns true. "abc"+de creates a new String object with a diff memory address, hence == compares it's memory address with "abcde", and as the addresses are diff, it returns false. – Infinite Recursion Jul 30 '14 at 05:11
  • 1
    "==" is shallow comparision. If two objects point to the same memory reference, it returns true. ".equals" is deep comparion. It compares two objects by value, if they contain the same value, it returns true. – Infinite Recursion Jul 30 '14 at 05:15

4 Answers4

13

The "problem" is simply that the compiler is too smart for you. When it sees "abc" + "de" it immediately concatenates that into the literal "abcde". But when it sees "abc" + de it's not allowed (per Java rules) to "optimize" that to a literal but must instead implement the + function, creating a new String object.

String literals are always handled as interned Strings, so == will work on them.

Hot Licks
  • 47,103
  • 17
  • 93
  • 151
  • 1
    Do you mean "abc"+"de" be part of string pool even before the program really begin to run? – Tony Jun 26 '14 at 01:57
  • 3
    @Tony - The compiler is designed such that a statement like `String a = "abc" + "de";` will be compiled as `String a = "abcde";`. This is by design, to allow multi-line string literals to be combined with `+` and other such things. – Hot Licks Jun 26 '14 at 02:02
  • 1
    Thanks for patience. One more question: when is the constant pool being loaded into JVM or memory. Thanks. – Tony Jun 26 '14 at 02:09
  • 1
    There is no "constant pool". When a class is loaded the string literals in the class are converted to interned String objects and pointers to those are stored in the in-storage representation of the class. – Hot Licks Jun 26 '14 at 02:16
  • 1
    Sorry, but I don't get it. Any references? – Tony Jun 26 '14 at 02:29
3

The problem isn't to do with the +, it's that you're comparing strings with ==.

Short answer: use "string".equals("string2")

In java == is a referential equals when used with objects (such as strings); "do these two names point at the same object in memory?"

Java has a string pool of common strings that it uses rather than creating a new object each time (which is not an issue as Strings are immutable), so your true/false issue is down to whether the compiler is clever enough to recognise that the two strings would be the same or not and that it can therefore use the same object. Don't rely on it.

Holloway
  • 6,412
  • 1
  • 26
  • 33
2
    String de = "de"; // "de" is set during compile time and placed in the "String Pool".
//  String abcde = "abc" + "de";// abcde == "abcde" reture true -- > String reference abcde will be set to "abcde" during compilation itself. and "abcde" will be placed in the String Pool.
    String abcde = "abc" + de;         // abcde == "abcde" reture false; abcde will not be set during compilation as the value of reference de will be resolved during runtime.
    System.out.println();
    System.out.println(abcde=="abcde");// false as --> String literal "abcde" on String pool != String Object "abcde" on heap.
    System.out.println(de=="de");// true--> de points to "de" on String pool
TheLostMind
  • 35,966
  • 12
  • 68
  • 104
1

It's an artifact of the Java compiler. As you should know, == compares object references, it doesn't compare their contents.

The Java compiler interns bare strings seen in the source code. This is why "x"=="x", as there is only one copy of the "x" string, interned from the source code, but reading in a String from a file whose value is "x" will not == that interned "x", because it's not the same object.

The Java compiler is also clever about the + operator on Strings. It will internally convert var+"x" to new StringBuffer().append(var).append("x").toString(). But it will also join strings together and intern them as yet another string, e.g. "abc"+"de" will result in the interned string "abcde", not new StringBuffer().append("abc").append("de").toString().

Stuart Caie
  • 2,803
  • 14
  • 15