Well, let's see. There are exactly three distinct String literals the compiler will put in t the class' constant pool, which are
hello,
m. and
hellomr. These three values end up as three distinct String instances in the String constant pool at runtime.
At runtime any invocation of the call() method will always return a reference to the String instance in the constant pool with the value
hello. When you assign that reference to two distinct variables
s1 and
s2 the referential equality check == will result in a boolean value true; the two variables refer to the exact same String instance after all. The same principle also applies to the assignment of variables
s5 and
s6 except that in this case the reference to the String instance is not returned from a method, but assigned directly.
So what's different about the case of
s3 and
s4? We know an invocation of
call() returns a reference to a String instance in the runtime String constant pool and we also know that the literal value
mr. ends up there as another String instance. Yet, somehow,
s3 and
s4 do not refer to the same String instance. It must have something to do with the fact that whatever is assigned to these variables is a concatenation of two values: the literal
mr. and the return value of the
call() method. Though, concatenation also takes place in the case of
s5 and
s6, so what gives? Well, in the latter case the compiler is smart enough to figure out that a concatenation of two literals will always yield the same value, and it simply puts that concatenated value (
hellomr.) in the class' constant pool. In the case of
s3 and
s4 the compiler can't know that! According to the compiler the method call could, at runtime, return a reference to a String that represents any given value, or it could even return null. In this particular program that is obviously not the the case, but the compiler isn't smart enough to pick up on that. Instead, at runtime,
s3 and
s4 are assigned unique String instances which are not in the String constant pool, but represent the same value. The referential equality check == will therefore yield false.
So, referential equality can be a tricky thing. This is why
you should usually (I won't say always) compare two String references (or any Object really) using the equals() method. In this case
System.out.println(s3.equals(s4)); would print true.