Four things are going on:
-
(You clearly know this, but for lurkers)
==tests to see if the variables point to the sameStringobject, not equivalent strings. So even ifxis"foo"andyis also"foo",x == ymay be true or false, depending on whetherxandyrefer to the sameStringobject or different ones. That’s why we useequals, 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. 🙂 -
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 == bis true. -
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
Stringobject is used (if not, the newStringobject for the new constant is added to the pool). So even ifxis a string constant initialized in classFooandyis a string constant initialized in classBar, 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.)
-
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., noStringBuilderis used, the concatenation happens at compile-time, not runtime. This is covered in JLS§15.28 – Constant Expressions. Soa == dis true for the same reasona == bis 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
cwill definitely be"dev"and thusewill 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 theStringBuildercode you referred to and that work is done at runtime, creating a newStringobject. That string isn’t automatically interned, soeends up referring to a differentStringobject thanadoes, and soa == eis false.Note that as Vinod said, if you declared
casfinal: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 == ewould also be true.
Just to reiterate: None of which means we should use == to compare strings for equivalence. 🙂 That’s what equals is for.