2

I've been using JShell a bit just to test it out, and today I came across quite the interesting bit of behavior.

jshell> String a = "A"
a ==> "A"

jshell> String b = "A"
b ==> "A"

jshell> a == b
$4 ==> true

jshell> "A" == "A"
$5 ==> true

I was first wondering if this was a feature of Java 9, and I checked it out by compiling and running this program with Java 9

public class Equus {
    public static void main(String... args) {
        String a = "A";
        String b = "A";
        System.out.println("a == b");
        System.out.println(a == b);
        System.out.println("\"A\" == \"A\"");
        System.out.println("A" == "A");
    }
}

And interestingly enough I got

a == b 
true 
"A" == "A" 
true

As my output as well. What's going on here? Why are a and b equal to each other and why is "A" == "A" true?

Eli Sadoff
  • 7,173
  • 6
  • 33
  • 61
  • 4
    This is called _interning_, and is something the compiler does to optimize itself. Instead of creating a duplicate String object for the new _A_, it uses the one it already has. [Relevant](http://stackoverflow.com/questions/10578984/what-is-string-interning). – Matt Clark Jan 19 '17 at 18:31
  • Is there some switch to prevent Java from interning strings without an explicit call to `String#intern`? – Eli Sadoff Jan 19 '17 at 18:31
  • 1
    You need to use `.equals()` to check whether or not the _Objects_ are equal, you are just checking the reference. – Matt Clark Jan 19 '17 at 18:33
  • 1
    @MattClark I know. I was wondering why they were the same reference. – Eli Sadoff Jan 19 '17 at 18:34
  • 4
    This is Java’s behavior since the very first version. Why should `"A" == "A"` ever be `false`? – Holger Jan 19 '17 at 18:49
  • 2
    this has nothing to do with java-9 – the8472 Jan 20 '17 at 14:07
  • 2
    Possible duplicate of [How do I compare strings in Java?](http://stackoverflow.com/questions/513832/how-do-i-compare-strings-in-java) – the8472 Jan 20 '17 at 14:07

2 Answers2

10

Why shouldn't it be? This behaviour is exhibited in previous Java versions as well - String literals are interned.

As you know, == checks for reference equality - the two variables have the same memory address. When a String is interned, all references of that string point to the intern pool and thus will be equal using ==.

Sinkingpoint
  • 7,449
  • 2
  • 29
  • 45
  • This furthermore explains why if I do `new String("A")` instead of `"A"` this doesn't work. – Eli Sadoff Jan 19 '17 at 18:33
  • 1
    @Eli Sadoff: note that the objects created via `new String("A")` and `"A"` will still refer to the same array internally… – Holger Jan 19 '17 at 18:50
  • @Holger They're both `{'A', '\0'}`, no? – Eli Sadoff Jan 19 '17 at 18:51
  • 4
    @Eli Sadoff: no, Java strings are never zero terminated. But I’m not referring to the array’s content anyway. I wanted to emphasize that they are pointing to the same array instance. So within the `String` class, `new String("A").value == "A".value` would be `true`. – Holger Jan 19 '17 at 18:54
  • @Holger Oh. That makes more sense. – Eli Sadoff Jan 19 '17 at 18:56
1

I just wanted to add this demonstration alongside Sinkingpoint's fine answer.
It's not safe to use == on Strings unless you know the origin of each, since a String that is built-up in some way (such as the new String("A") in Eli's comment or the .toString() used here) is not the same reference even if the two do use the same underlying character array.

class Main
{
  public static void main(String[] args)
  {
    String oneA = "A";
    String twoA = "A";
    String three = new StringBuilder().append('A').toString();

    // We expect constant literals to be ==
    System.out.print("A == A -> ");
    System.out.println("A" == "A");

    // Variables assigned the same literal are also ==
    System.out.print("oneA == twoA -> ");
    System.out.println(oneA == twoA);

    // but a built-up String var is not == to the "literal" var.
    System.out.print("oneA == three -> ");
    System.out.println(oneA == three);

    // If we intern() them they are again ==
    System.out.print("oneA.intern() == three.intern() -> ");
    System.out.println(oneA.intern() == three.intern());

    // and of course, .equals() is always safe.
    System.out.print("oneA .equals three -> ");
    System.out.println(oneA.equals(three));
  }
}

The output from this (run on https://repl.it/languages/java) is:

A == A -> true
oneA == twoA -> true
oneA == three -> false
oneA.intern() == three.intern() -> true
oneA .equals three -> true

You can safely use string1.equals(string2) or string1.intern() == string2.intern()

Stephen P
  • 14,422
  • 2
  • 43
  • 67